"""CME data service for Edge API SDK."""

from typing import Optional, Union, List, Dict, Any
from datetime import date, datetime
import pandas as pd


class CMEService:
    """Service for accessing CME futures and options data."""

    def __init__(self, client):
        """Initialize with parent client."""
        self.client = client
        self.source_name = "CME"  # Direct CME data access

    def get_futures_prices(
        self,
        symbol: str,
        start_date: Optional[Union[str, date, datetime]] = None,
        end_date: Optional[Union[str, date, datetime]] = None,
        limit: Optional[int] = 500,
    ) -> pd.DataFrame:
        """
        Get futures prices for a CME contract.

        Args:
            symbol: Futures symbol (e.g., 'ZCZ5' for Corn Dec 2025)
            start_date: Start date for data
            end_date: End date for data
            limit: Maximum records to return

        Returns:
            DataFrame with futures OHLCV data
        """
        # Simply use the get_symbol_data method
        return self.get_symbol_data(symbol, start_date, end_date, limit)

    def get_options_data(
        self,
        underlying: str,
        strike: Optional[float] = None,
        expiry: Optional[str] = None,
        start_date: Optional[Union[str, date, datetime]] = None,
        end_date: Optional[Union[str, date, datetime]] = None,
    ) -> pd.DataFrame:
        """
        Get options data for CME contracts.

        Args:
            underlying: Underlying futures symbol
            strike: Option strike price
            expiry: Option expiry date
            start_date: Start date for data
            end_date: End date for data

        Returns:
            DataFrame with options data
        """
        # Build series code for options
        series_code = f"{underlying}_OPTIONS"
        if strike:
            series_code += f"_{strike}"
        if expiry:
            series_code += f"_{expiry}"

        return self.client.time_series.get_data(
            series_code=series_code,
            source_name=self.source_name,
            start_date=start_date,
            end_date=end_date,
        )

    def get_spread(
        self,
        front_contract: str,
        back_contract: str,
        start_date: Optional[Union[str, date, datetime]] = None,
        end_date: Optional[Union[str, date, datetime]] = None,
    ) -> pd.DataFrame:
        """
        Get spread between two futures contracts.

        Args:
            front_contract: Front month contract (e.g., 'HEZ24')
            back_contract: Back month contract (e.g., 'HEG25')
            start_date: Start date
            end_date: End date

        Returns:
            DataFrame with spread data
        """
        # Get data for both contracts
        configs = [
            {"series_code": front_contract, "source_name": self.source_name},
            {"series_code": back_contract, "source_name": self.source_name},
        ]

        data = self.client.time_series.get_multiple(
            series_configs=configs,
            start_date=start_date,
            end_date=end_date,
        )

        if data.empty:
            return pd.DataFrame()

        # Calculate spread
        front_data = data[data["series_code"] == front_contract].copy()
        back_data = data[data["series_code"] == back_contract].copy()

        if front_data.empty or back_data.empty:
            return pd.DataFrame()

        # Merge on date
        spread_df = pd.merge(
            front_data[["observation_date", "value"]],
            back_data[["observation_date", "value"]],
            on="observation_date",
            suffixes=("_front", "_back"),
        )

        spread_df["spread"] = spread_df["value_front"] - spread_df["value_back"]
        spread_df["front_contract"] = front_contract
        spread_df["back_contract"] = back_contract

        return spread_df

    def get_continuous_contract(
        self,
        symbol: str,
        start_date: Optional[Union[str, date, datetime]] = None,
        end_date: Optional[Union[str, date, datetime]] = None,
    ) -> pd.DataFrame:
        """
        Get continuous contract data (front month rolled).

        Args:
            symbol: Base symbol (e.g., 'HE', 'LE', 'GF')
            start_date: Start date
            end_date: End date

        Returns:
            DataFrame with continuous contract data
        """
        series_code = f"{symbol}_CONTINUOUS"

        return self.client.time_series.get_data(
            series_code=series_code,
            source_name=self.source_name,
            start_date=start_date,
            end_date=end_date,
        )

    def list_available_contracts(self) -> pd.DataFrame:
        """
        List all available CME contracts.

        Returns:
            DataFrame with available CME contracts
        """
        # Search for all CME/Barchart series
        return self.client.time_series.get_metadata(
            source_name=self.source_name,
            theme_name="PUBLIC_MARKETS",
            limit=500
        )

    def list_symbols(self) -> List[str]:
        """
        List all available CME symbols with data.

        Returns:
            List of available CME symbols
        """
        return self.client._get("/data/cme/symbols")

    def get_symbol_data(
        self,
        symbol: str,
        start_date: Optional[Union[str, date, datetime]] = None,
        end_date: Optional[Union[str, date, datetime]] = None,
        limit: int = 500,
    ) -> pd.DataFrame:
        """
        Get CME time series data for a specific symbol.

        Args:
            symbol: CME symbol (e.g., 'ZCH5' for Corn March 2025)
            start_date: Start date for data
            end_date: End date for data
            limit: Maximum records to return

        Returns:
            DataFrame with OHLCV and market data
        """
        params = {"limit": limit}

        if start_date:
            if isinstance(start_date, (date, datetime)):
                params["start_date"] = start_date.isoformat()
            else:
                params["start_date"] = str(start_date)

        if end_date:
            if isinstance(end_date, (date, datetime)):
                params["end_date"] = end_date.isoformat()
            else:
                params["end_date"] = str(end_date)

        response = self.client._get(f"/data/cme/data/{symbol}", params=params)

        if "data" in response:
            df = pd.DataFrame(response["data"])
            if not df.empty:
                # Convert datetime to pandas datetime and rename for consistency
                df["observation_datetime"] = pd.to_datetime(df["datetime"])
                df = df.sort_values("observation_datetime")
            return df

        return pd.DataFrame()

    def get_spread_data(
        self,
        spread_symbol: str,
        start_date: Optional[Union[str, date, datetime]] = None,
        end_date: Optional[Union[str, date, datetime]] = None,
        limit: int = 500,
    ) -> pd.DataFrame:
        """
        Get CME calendar spread data.

        Args:
            spread_symbol: Spread symbol (e.g., 'ZCH5-ZCN5')
            start_date: Start date for data
            end_date: End date for data
            limit: Maximum records to return

        Returns:
            DataFrame with spread OHLCV data
        """
        params = {"limit": limit}

        if start_date:
            if isinstance(start_date, (date, datetime)):
                params["start_date"] = start_date.isoformat()
            else:
                params["start_date"] = str(start_date)

        if end_date:
            if isinstance(end_date, (date, datetime)):
                params["end_date"] = end_date.isoformat()
            else:
                params["end_date"] = str(end_date)

        response = self.client._get(f"/data/cme/spreads/{spread_symbol}", params=params)

        if "data" in response:
            df = pd.DataFrame(response["data"])
            if not df.empty:
                # Convert datetime to pandas datetime and rename for consistency
                df["observation_datetime"] = pd.to_datetime(df["datetime"])
                df = df.sort_values("observation_datetime")
            return df

        return pd.DataFrame()