"""EODHD (EOD Historical Data) provider.

Global market data covering 70+ exchanges, fundamentals, and corporate actions.
Free tier: 20 API calls/day. Premium tiers available.

Docs: https://eodhd.com/financial-apis
"""

from __future__ import annotations

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

import requests

from ..models import Bar, Quote, TimeFrame


class EODHDProvider:
    """EODHD data provider.

    Supports stocks, ETFs, bonds, forex, crypto, fundamentals,
    dividends, and splits across 70+ global exchanges.

    Usage:
        provider = EODHDProvider(api_key="your_key")
        bars = provider.get_bars("AAPL.US", start=datetime(2023, 1, 1))
    """

    name = "eodhd"
    supported_assets = ["stocks", "etfs", "bonds", "forex", "crypto", "fundamentals"]
    BASE_URL = "https://eodhd.com"

    def __init__(self, api_key: Optional[str] = None) -> None:
        """Initialize EODHD provider.

        Args:
            api_key: EODHD API key. Get one at eodhd.com
        """
        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="eodhd")
                self._api_key = creds.get("eodhd_api_key", "")
            except Exception:
                pass

        if not self._api_key:
            print("[CPZ SDK] EODHD API key not found. Get a key at eodhd.com")

    def _request(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
        """Make authenticated API request."""
        params = dict(params) if params else {}
        params["api_token"] = self._api_key
        params.setdefault("fmt", "json")
        url = f"{self.BASE_URL}{path}"
        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        return response.json()

    def _convert_timeframe(self, tf: TimeFrame) -> tuple[bool, str]:
        """Convert TimeFrame to EODHD interval.

        Returns:
            Tuple of (is_intraday, interval string)
        """
        intraday_map = {
            TimeFrame.MINUTE_1: "1m",
            TimeFrame.MINUTE_5: "5m",
            TimeFrame.MINUTE_15: "15m",
            TimeFrame.MINUTE_30: "30m",
            TimeFrame.HOUR_1: "1h",
        }
        if tf in intraday_map:
            return True, intraday_map[tf]
        return False, ""

    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 bars from EODHD.

        Args:
            symbol: Symbol with exchange suffix (e.g., "AAPL.US")
            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)

        is_intraday, interval = self._convert_timeframe(timeframe)

        try:
            params: Dict[str, Any] = {}
            if start:
                params["from"] = start.strftime("%Y-%m-%d")
            if end:
                params["to"] = end.strftime("%Y-%m-%d")

            if is_intraday:
                path = f"/api/intraday/{symbol}"
                params["interval"] = interval
                data = self._request(path, params)
                bars = self._parse_intraday_bars(data, symbol)
            else:
                path = f"/api/eod/{symbol}"
                data = self._request(path, params)
                bars = self._parse_eod_bars(data, symbol)

            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 EODHD data for {symbol}: {e}")
            return []

    def _parse_eod_bars(self, data: List[Dict], symbol: str) -> List[Bar]:
        """Parse end-of-day endpoint response into Bar objects."""
        bars: List[Bar] = []
        for row in data:
            try:
                timestamp = datetime.strptime(row["date"], "%Y-%m-%d")
                bars.append(Bar(
                    symbol=symbol,
                    timestamp=timestamp,
                    open=float(row.get("open", 0)),
                    high=float(row.get("high", 0)),
                    low=float(row.get("low", 0)),
                    close=float(row.get("adjusted_close", row.get("close", 0))),
                    volume=float(row.get("volume", 0)),
                    vwap=None,
                ))
            except (ValueError, KeyError):
                continue
        return bars

    def _parse_intraday_bars(self, data: List[Dict], symbol: str) -> List[Bar]:
        """Parse intraday endpoint response into Bar objects."""
        bars: List[Bar] = []
        for row in data:
            try:
                ts_str = row.get("datetime", row.get("date", ""))
                if "T" in ts_str or " " in ts_str:
                    timestamp = datetime.strptime(ts_str[:19].replace("T", " "), "%Y-%m-%d %H:%M:%S")
                else:
                    timestamp = datetime.strptime(ts_str[:10], "%Y-%m-%d")
                bars.append(Bar(
                    symbol=symbol,
                    timestamp=timestamp,
                    open=float(row.get("open", 0)),
                    high=float(row.get("high", 0)),
                    low=float(row.get("low", 0)),
                    close=float(row.get("close", 0)),
                    volume=float(row.get("volume", 0)),
                    vwap=None,
                ))
            except (ValueError, KeyError):
                continue
        return bars

    def get_quote(self, symbol: str) -> Optional[Quote]:
        """Get real-time quote from EODHD."""
        try:
            data = self._request(f"/api/real-time/{symbol}")

            if not data:
                return None

            price = float(data.get("close", data.get("previousClose", 0)))

            return Quote(
                symbol=symbol,
                timestamp=datetime.utcnow(),
                bid=price,
                ask=price,
                bid_size=0,
                ask_size=0,
            )

        except Exception as e:
            print(f"[CPZ SDK] Error fetching EODHD quote for {symbol}: {e}")
            return None

    def get_fundamentals(self, symbol: str) -> Dict[str, Any]:
        """Get fundamental data for a symbol.

        Args:
            symbol: Symbol with exchange suffix (e.g., "AAPL.US")

        Returns:
            Dict with fundamental data (financials, valuation, etc.)
        """
        try:
            return self._request(f"/api/fundamentals/{symbol}")
        except Exception as e:
            print(f"[CPZ SDK] Error fetching EODHD fundamentals for {symbol}: {e}")
            return {}

    def get_dividends(
        self,
        symbol: str,
        start: Optional[datetime] = None,
        end: Optional[datetime] = None,
    ) -> List[Dict[str, Any]]:
        """Get dividend history.

        Args:
            symbol: Symbol with exchange suffix (e.g., "AAPL.US")
            start: Start date
            end: End date

        Returns:
            List of dividend records
        """
        try:
            params: Dict[str, Any] = {}
            if start:
                params["from"] = start.strftime("%Y-%m-%d")
            if end:
                params["to"] = end.strftime("%Y-%m-%d")
            data = self._request(f"/api/div/{symbol}", params)
            return data if isinstance(data, list) else []
        except Exception as e:
            print(f"[CPZ SDK] Error fetching EODHD dividends for {symbol}: {e}")
            return []

    def get_splits(
        self,
        symbol: str,
        start: Optional[datetime] = None,
        end: Optional[datetime] = None,
    ) -> List[Dict[str, Any]]:
        """Get stock split history.

        Args:
            symbol: Symbol with exchange suffix (e.g., "AAPL.US")
            start: Start date
            end: End date

        Returns:
            List of split records
        """
        try:
            params: Dict[str, Any] = {}
            if start:
                params["from"] = start.strftime("%Y-%m-%d")
            if end:
                params["to"] = end.strftime("%Y-%m-%d")
            data = self._request(f"/api/splits/{symbol}", params)
            return data if isinstance(data, list) else []
        except Exception as e:
            print(f"[CPZ SDK] Error fetching EODHD splits for {symbol}: {e}")
            return []
