"""
Yahoo Finance provider using yfinance library
"""
from typing import Optional, List, Dict
from datetime import datetime
import pandas as pd

try:
    import yfinance as yf
except ImportError:
    yf = None

from .base import BaseProvider
from ..models import Quote, HistoricalData, QuoteType
from ..core import ProviderRateLimiter
from .yfinance_extensions import (
    get_calendar_yfinance,
    get_recommendations_yfinance,
    get_financials_yfinance
)


class YFinanceProvider(BaseProvider):
    """Yahoo Finance data provider using yfinance"""
    
    def __init__(self, rate_limiter: Optional[ProviderRateLimiter] = None):
        super().__init__("yfinance")
        self.rate_limiter = rate_limiter
        
        if yf is None:
            self._available = False
            self._last_error = "yfinance not installed"
    
    def get_quote(self, symbol: str) -> Optional[Quote]:
        """Get real-time quote"""
        if not self._available:
            return None
        
        try:
            if self.rate_limiter:
                self.rate_limiter.wait_if_needed("yfinance")
            
            ticker = yf.Ticker(symbol)
            info = ticker.info
            
            # Get current price
            price = info.get("currentPrice") or info.get("regularMarketPrice")
            if not price:
                return None
            
            # Build quote object
            quote = Quote(
                symbol=symbol,
                timestamp=datetime.now(),
                price=price,
                open=info.get("regularMarketOpen"),
                high=info.get("regularMarketDayHigh"),
                low=info.get("regularMarketDayLow"),
                close=info.get("regularMarketPrice"),
                volume=info.get("regularMarketVolume"),
                bid=info.get("bid"),
                ask=info.get("ask"),
                bid_size=info.get("bidSize"),
                ask_size=info.get("askSize"),
                change=info.get("regularMarketChange"),
                change_percent=info.get("regularMarketChangePercent"),
                previous_close=info.get("previousClose"),
                market_cap=info.get("marketCap"),
                pe_ratio=info.get("trailingPE"),
                fifty_two_week_high=info.get("fiftyTwoWeekHigh"),
                fifty_two_week_low=info.get("fiftyTwoWeekLow"),
                exchange=info.get("exchange"),
                currency=info.get("currency"),
                source="yfinance",
                metadata={"quote_type": info.get("quoteType")}
            )
            
            return quote
            
        except Exception as e:
            self._handle_error(e, f"get_quote({symbol})")
            return None
    
    def get_historical(self,
                      symbol: str,
                      start: datetime,
                      end: datetime,
                      interval: str = "1d") -> Optional[HistoricalData]:
        """Get historical data"""
        if not self._available:
            return None
        
        try:
            if self.rate_limiter:
                self.rate_limiter.wait_if_needed("yfinance")
            
            ticker = yf.Ticker(symbol)
            
            # Map interval format
            interval_map = {
                "1m": "1m", "5m": "5m", "15m": "15m", "30m": "30m",
                "1h": "1h", "1d": "1d", "1wk": "1wk", "1mo": "1mo"
            }
            yf_interval = interval_map.get(interval, "1d")
            
            # Download data
            df = ticker.history(
                start=start,
                end=end,
                interval=yf_interval
            )
            
            if df.empty:
                return None
            
            # Standardize column names
            df.columns = [col.lower() for col in df.columns]
            df.reset_index(inplace=True)
            
            # Rename columns to standard format
            column_map = {
                "date": "date",
                "open": "open",
                "high": "high",
                "low": "low",
                "close": "close",
                "volume": "volume"
            }
            df = df.rename(columns=column_map)
            
            return HistoricalData(
                symbol=symbol,
                data=df,
                start_date=start,
                end_date=end,
                interval=interval,
                source="yfinance"
            )
            
        except Exception as e:
            self._handle_error(e, f"get_historical({symbol})")
            return None
    
    def get_multiple_quotes(self, symbols: List[str]) -> List[Quote]:
        """
        Get quotes for multiple symbols using yf.download()
        
        NOTE: yfinance 0.2.66+ has a bug where yf.Tickers() + ticker.info 
        can block indefinitely. Using yf.download() as workaround.
        """
        quotes = []
        
        if not self._available or not symbols:
            return quotes
        
        try:
            if self.rate_limiter:
                self.rate_limiter.wait_if_needed("yfinance")
            
            # Use yf.download() instead of yf.Tickers() to avoid blocking issue
            # This is much more reliable and faster for batch operations
            df = yf.download(
                symbols,
                period="1d",
                interval="1d",
                progress=False,
                threads=False,  # Disable threading to avoid issues
                auto_adjust=False  # Get raw prices
            )
            
            if df.empty:
                # Fallback to individual requests if download fails
                return self._fallback_individual_quotes(symbols)
            
            # Handle both single and multiple symbol cases
            if len(symbols) == 1:
                # Single symbol: columns are simple (Open, High, Low, etc.)
                symbol = symbols[0]
                try:
                    latest = df.iloc[-1]
                    quote = Quote(
                        symbol=symbol,
                        timestamp=datetime.now(),
                        price=latest.get('Close', latest.get('Adj Close')),
                        open=latest.get('Open'),
                        high=latest.get('High'),
                        low=latest.get('Low'),
                        close=latest.get('Close'),
                        volume=latest.get('Volume'),
                        previous_close=df.iloc[-2]['Close'] if len(df) > 1 else None,
                        source="yfinance",
                        metadata={"method": "download"}
                    )
                    quotes.append(quote)
                except Exception as e:
                    self._handle_error(e, f"parse_single({symbol})")
            else:
                # Multiple symbols: columns are MultiIndex (Price, Ticker)
                for symbol in symbols:
                    try:
                        # Extract data for this symbol
                        if 'Close' in df.columns:
                            # Check if MultiIndex
                            if isinstance(df.columns, pd.MultiIndex):
                                close_val = df[('Close', symbol)].iloc[-1] if ('Close', symbol) in df.columns else None
                                open_val = df[('Open', symbol)].iloc[-1] if ('Open', symbol) in df.columns else None
                                high_val = df[('High', symbol)].iloc[-1] if ('High', symbol) in df.columns else None
                                low_val = df[('Low', symbol)].iloc[-1] if ('Low', symbol) in df.columns else None
                                volume_val = df[('Volume', symbol)].iloc[-1] if ('Volume', symbol) in df.columns else None
                                prev_close = df[('Close', symbol)].iloc[-2] if len(df) > 1 and ('Close', symbol) in df.columns else None
                            else:
                                # Fallback for non-MultiIndex (shouldn't happen with multiple symbols)
                                continue
                        else:
                            continue
                        
                        if close_val and pd.notna(close_val):
                            quote = Quote(
                                symbol=symbol,
                                timestamp=datetime.now(),
                                price=float(close_val),
                                open=float(open_val) if pd.notna(open_val) else None,
                                high=float(high_val) if pd.notna(high_val) else None,
                                low=float(low_val) if pd.notna(low_val) else None,
                                close=float(close_val),
                                volume=int(volume_val) if pd.notna(volume_val) else None,
                                previous_close=float(prev_close) if pd.notna(prev_close) else None,
                                source="yfinance",
                                metadata={"method": "download"}
                            )
                            quotes.append(quote)
                    except Exception as e:
                        self._handle_error(e, f"parse_multi({symbol})")
                        continue
                    
        except Exception as e:
            self._handle_error(e, "get_multiple_quotes")
            # Fallback to individual requests
            return self._fallback_individual_quotes(symbols)
        
        return quotes
    
    def _fallback_individual_quotes(self, symbols: List[str]) -> List[Quote]:
        """Fallback method: fetch quotes individually"""
        quotes = []
        for symbol in symbols:
            try:
                quote = self.get_quote(symbol)
                if quote:
                    quotes.append(quote)
            except:
                continue
        return quotes
    
    # ========================================================================
    # Extended APIs
    # ========================================================================
    
    def get_calendar(self, symbol: str) -> Dict:
        """Get upcoming events calendar"""
        if not self._available:
            return {}
        return get_calendar_yfinance(symbol)
    
    def get_recommendations(self, symbol: str) -> pd.DataFrame:
        """Get analyst recommendations"""
        if not self._available:
            return pd.DataFrame()
        return get_recommendations_yfinance(symbol)
    
    def get_financials(self, symbol: str, statement: str, frequency: str) -> pd.DataFrame:
        """Get financial statements"""
        if not self._available:
            return pd.DataFrame()
        return get_financials_yfinance(symbol, statement, frequency)
