"""Financial Modeling Prep (FMP) data provider.

Comprehensive financial data API with fundamentals, quotes, and historical prices.
Free tier available with limitations. Premium tiers for full access.

Docs: https://site.financialmodelingprep.com/developer/docs
"""

from __future__ import annotations

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

import requests

from ..models import Bar, Quote, TimeFrame


class FMPProvider:
    """Financial Modeling Prep data provider.

    Supports stocks, financials, profiles, ratios, and historical prices.

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

    name = "fmp"
    supported_assets = ["stocks", "forex", "crypto", "commodities"]
    BASE_URL = "https://financialmodelingprep.com"

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

        Args:
            api_key: FMP API key. Get one at financialmodelingprep.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="fmp")
                self._api_key = creds.get("fmp_api_key", "")
            except Exception:
                pass

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

    def _request(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
        """Make API request."""
        url = f"{self.BASE_URL}{path}"
        if params is None:
            params = {}
        params["apikey"] = self._api_key

        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        return response.json()

    def _convert_timeframe(self, tf: TimeFrame) -> str:
        """Convert TimeFrame to FMP interval string for intraday charts."""
        interval_map = {
            TimeFrame.MINUTE_1: "1min",
            TimeFrame.MINUTE_5: "5min",
            TimeFrame.MINUTE_15: "15min",
            TimeFrame.MINUTE_30: "30min",
            TimeFrame.HOUR_1: "1hour",
            TimeFrame.HOUR_4: "4hour",
        }
        return interval_map.get(tf, "1hour")

    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 FMP.

        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)

        is_intraday = timeframe in (
            TimeFrame.MINUTE_1, TimeFrame.MINUTE_5, TimeFrame.MINUTE_15,
            TimeFrame.MINUTE_30, TimeFrame.HOUR_1, TimeFrame.HOUR_4,
        )

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

        try:
            if is_intraday:
                interval = self._convert_timeframe(timeframe)
                data = self._request(
                    f"/stable/historical-chart/{interval}/{symbol}",
                    params=params,
                )
                raw_bars = data if isinstance(data, list) else []
            else:
                data = self._request(
                    f"/stable/historical-price-eod/full/{symbol}",
                    params=params,
                )
                if isinstance(data, dict):
                    raw_bars = data.get("historical", [])
                elif isinstance(data, list):
                    raw_bars = data
                else:
                    raw_bars = []

            bars: List[Bar] = []
            for item in raw_bars:
                try:
                    date_str = item.get("date", "")
                    timestamp = datetime.strptime(date_str.split()[0], "%Y-%m-%d")

                    if start and timestamp < start:
                        continue
                    if end and timestamp > end:
                        continue

                    bars.append(Bar(
                        symbol=symbol,
                        timestamp=timestamp,
                        open=float(item.get("open", 0)),
                        high=float(item.get("high", 0)),
                        low=float(item.get("low", 0)),
                        close=float(item.get("close", 0)),
                        volume=float(item.get("volume", 0)),
                        vwap=None,
                    ))
                except (ValueError, KeyError):
                    continue

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

    def get_quote(self, symbol: str) -> Optional[Quote]:
        """Get latest quote from FMP.

        Args:
            symbol: Stock symbol

        Returns:
            Quote object or None
        """
        try:
            data = self._request(f"/api/v3/quote/{symbol}")

            if isinstance(data, list) and data:
                item = data[0]
            elif isinstance(data, dict):
                item = data
            else:
                return None

            price = float(item.get("price", 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 FMP quote for {symbol}: {e}")
            return None

    def get_quotes(self, symbols: List[str]) -> List[Quote]:
        """Get quotes for multiple symbols.

        Args:
            symbols: List of stock symbols

        Returns:
            List of Quote objects
        """
        quotes: List[Quote] = []
        for symbol in symbols:
            quote = self.get_quote(symbol)
            if quote:
                quotes.append(quote)
        return quotes

    def get_financials(
        self,
        symbol: str,
        period: str = "annual",
        limit: int = 10,
    ) -> List[Dict[str, Any]]:
        """Get income statement data.

        Args:
            symbol: Stock symbol
            period: "annual" or "quarter"
            limit: Number of periods

        Returns:
            List of financial statement dicts
        """
        try:
            data = self._request(
                f"/api/v3/income-statement/{symbol}",
                params={"period": period, "limit": limit},
            )
            return data if isinstance(data, list) else []
        except Exception as e:
            print(f"[CPZ SDK] Error fetching FMP financials for {symbol}: {e}")
            return []

    def get_profile(self, symbol: str) -> Dict[str, Any]:
        """Get company profile.

        Args:
            symbol: Stock symbol

        Returns:
            Dict with company profile data
        """
        try:
            data = self._request(f"/api/v3/profile/{symbol}")
            if isinstance(data, list) and data:
                return data[0]
            return data if isinstance(data, dict) else {}
        except Exception as e:
            print(f"[CPZ SDK] Error fetching FMP profile for {symbol}: {e}")
            return {}

    def get_ratios(
        self,
        symbol: str,
        period: str = "annual",
    ) -> List[Dict[str, Any]]:
        """Get financial ratios.

        Args:
            symbol: Stock symbol
            period: "annual" or "quarter"

        Returns:
            List of ratio dicts
        """
        try:
            data = self._request(
                f"/api/v3/ratios/{symbol}",
                params={"period": period},
            )
            return data if isinstance(data, list) else []
        except Exception as e:
            print(f"[CPZ SDK] Error fetching FMP ratios for {symbol}: {e}")
            return []
