"""Finnhub data provider.

Real-time stock data, news, earnings, and analyst recommendations.
Free tier: 60 API calls/minute.

Docs: https://finnhub.io/docs/api
"""

from __future__ import annotations

from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional

import requests

from ..models import Bar, Quote, TimeFrame


class FinnhubProvider:
    """Finnhub data provider.
    
    Supports stocks, news, earnings, and analyst recommendations.
    
    Usage:
        provider = FinnhubProvider(api_key="your_key")
        bars = provider.get_bars("AAPL", start=datetime(2023, 1, 1))
    """
    
    name = "finnhub"
    BASE_URL = "https://finnhub.io"
    
    RESOLUTION_MAP = {
        TimeFrame.MINUTE_1: "1",
        TimeFrame.MINUTE_5: "5",
        TimeFrame.MINUTE_15: "15",
        TimeFrame.MINUTE_30: "30",
        TimeFrame.HOUR_1: "60",
        TimeFrame.HOUR_4: "D",
        TimeFrame.DAY: "D",
        TimeFrame.WEEK: "W",
        TimeFrame.MONTH: "M",
    }
    
    def __init__(self, api_key: Optional[str] = None) -> None:
        """Initialize Finnhub provider.
        
        Args:
            api_key: Finnhub API key. Get free key at finnhub.io
        """
        self._api_key = api_key or ""
        
        if not self._api_key:
            try:
                from ...common.cpz_ai import CPZAIClient
                cpz_client = CPZAIClient.from_env()
                creds = cpz_client.get_data_credentials(provider="finnhub")
                self._api_key = creds.get("finnhub_api_key", "")
            except Exception:
                pass
        
        if not self._api_key:
            print("[CPZ SDK] Finnhub API key not found. Get a free key at finnhub.io")
    
    def _request(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
        """Make API request with token auth."""
        if params is None:
            params = {}
        params["token"] = self._api_key
        
        url = f"{self.BASE_URL}{path}"
        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        return response.json()
    
    def get_bars(
        self,
        symbol: str,
        timeframe: TimeFrame | str = TimeFrame.DAY,
        start: Optional[datetime] = None,
        end: Optional[datetime] = None,
        limit: Optional[int] = None,
    ) -> List[Bar]:
        """Get historical candle data from Finnhub.
        
        Args:
            symbol: Stock symbol (e.g., "AAPL")
            timeframe: Bar timeframe
            start: Start datetime
            end: End datetime
            limit: Maximum bars
            
        Returns:
            List of Bar objects
        """
        if isinstance(timeframe, str):
            timeframe = TimeFrame.from_string(timeframe)
        
        resolution = self.RESOLUTION_MAP.get(timeframe, "D")
        
        if not start:
            start = datetime.utcnow() - timedelta(days=365)
        if not end:
            end = datetime.utcnow()
        
        from_ts = int(start.timestamp())
        to_ts = int(end.timestamp())
        
        try:
            data = self._request("/api/v1/stock/candle", params={
                "symbol": symbol,
                "resolution": resolution,
                "from": from_ts,
                "to": to_ts,
            })
            
            if data.get("s") != "ok":
                print(f"[CPZ SDK] No Finnhub data for {symbol}: {data.get('s', 'unknown')}")
                return []
            
            timestamps = data.get("t", [])
            opens = data.get("o", [])
            highs = data.get("h", [])
            lows = data.get("l", [])
            closes = data.get("c", [])
            volumes = data.get("v", [])
            
            bars: List[Bar] = []
            for i in range(len(timestamps)):
                bars.append(Bar(
                    symbol=symbol,
                    timestamp=datetime.utcfromtimestamp(timestamps[i]),
                    open=float(opens[i]),
                    high=float(highs[i]),
                    low=float(lows[i]),
                    close=float(closes[i]),
                    volume=float(volumes[i]) if i < len(volumes) else 0.0,
                    vwap=None,
                ))
            
            bars.sort(key=lambda x: x.timestamp)
            
            if limit and len(bars) > limit:
                bars = bars[-limit:]
            
            return bars
            
        except Exception as e:
            print(f"[CPZ SDK] Error fetching Finnhub data for {symbol}: {e}")
            return []
    
    def get_quote(self, symbol: str) -> Optional[Quote]:
        """Get latest quote from Finnhub.
        
        Response fields: c (current), h (high), l (low), o (open), pc (prev close), t (timestamp).
        """
        try:
            data = self._request("/api/v1/quote", params={"symbol": symbol})
            
            price = data.get("c")
            if price is None or price == 0:
                return None
            
            return Quote(
                symbol=symbol,
                timestamp=datetime.utcfromtimestamp(data.get("t", 0)) if data.get("t") else datetime.utcnow(),
                bid=float(price),
                ask=float(price),
                bid_size=0,
                ask_size=0,
            )
            
        except Exception as e:
            print(f"[CPZ SDK] Error fetching Finnhub quote for {symbol}: {e}")
            return None
    
    def get_quotes(self, symbols: List[str]) -> List[Quote]:
        """Get quotes for multiple symbols."""
        quotes: List[Quote] = []
        for symbol in symbols:
            quote = self.get_quote(symbol)
            if quote:
                quotes.append(quote)
        return quotes
    
    def get_news(
        self,
        symbol: str,
        start: Optional[datetime] = None,
        end: Optional[datetime] = None,
    ) -> List[Dict[str, Any]]:
        """Get company news from Finnhub.
        
        Args:
            symbol: Stock symbol
            start: Start date
            end: End date
            
        Returns:
            List of news article dicts
        """
        if not start:
            start = datetime.utcnow() - timedelta(days=7)
        if not end:
            end = datetime.utcnow()
        
        try:
            return self._request("/api/v1/company-news", params={
                "symbol": symbol,
                "from": start.strftime("%Y-%m-%d"),
                "to": end.strftime("%Y-%m-%d"),
            })
        except Exception as e:
            print(f"[CPZ SDK] Error fetching Finnhub news for {symbol}: {e}")
            return []
    
    def get_recommendation(self, symbol: str) -> List[Dict[str, Any]]:
        """Get analyst recommendation trends.
        
        Args:
            symbol: Stock symbol
            
        Returns:
            List of recommendation dicts with buy/hold/sell/strongBuy/strongSell counts
        """
        try:
            return self._request("/api/v1/stock/recommendation", params={"symbol": symbol})
        except Exception as e:
            print(f"[CPZ SDK] Error fetching Finnhub recommendations for {symbol}: {e}")
            return []
    
    def get_earnings(self, symbol: str) -> List[Dict[str, Any]]:
        """Get company earnings surprises.
        
        Args:
            symbol: Stock symbol
            
        Returns:
            List of earnings dicts with actual/estimate/period/surprise
        """
        try:
            return self._request("/api/v1/stock/earnings", params={"symbol": symbol})
        except Exception as e:
            print(f"[CPZ SDK] Error fetching Finnhub earnings for {symbol}: {e}")
            return []
