from datetime import datetime
from warnings import warn

import httpx
import pandas as pd
from schwab.client import AsyncClient, Client

from ._types import SchwabPriceHistoryInterval, SchwabPriceHistoryIntervalStr
from .ticker import normalize_ticker


def _warn_if_futures(symbol: str) -> None:
    """Warn if the symbol appears to be a futures contract."""
    if symbol.startswith("/"):
        warn(
            f"Symbol '{symbol}' appears to be a futures contract. "
            "Schwab's price history API only supports equities and ETFs, not futures. "
            "You may receive limited or no data. For futures data, consider using "
            "Schwab's streaming API (chart_futures_subs) or an alternative data source.",
            UserWarning,
            stacklevel=3,
        )


# Mapping from interval to schwab-py method name
_INTERVAL_TO_METHOD: dict[
    SchwabPriceHistoryInterval | SchwabPriceHistoryIntervalStr, str
] = {
    "1": "get_price_history_every_minute",
    "5": "get_price_history_every_five_minutes",
    "10": "get_price_history_every_ten_minutes",
    "15": "get_price_history_every_fifteen_minutes",
    "30": "get_price_history_every_thirty_minutes",
    1: "get_price_history_every_minute",
    5: "get_price_history_every_five_minutes",
    10: "get_price_history_every_ten_minutes",
    15: "get_price_history_every_fifteen_minutes",
    30: "get_price_history_every_thirty_minutes",
    "d": "get_price_history_every_day",
    "w": "get_price_history_every_week",
}


def price_history_to_df(price_history: dict | httpx.Response) -> pd.DataFrame:
    """
    Convert Schwab price history response to a pandas DataFrame.

    Args:
        price_history: Raw response from Schwab API (dict or httpx.Response)

    Returns:
        DataFrame with columns: open, high, low, close, volume, timestamp, datetime, date
    """
    if isinstance(price_history, httpx.Response):
        price_history = price_history.json()

    if price_history.get("empty", True):
        return pd.DataFrame()

    candles = price_history.get("candles")
    if not candles:
        return pd.DataFrame()

    df = pd.DataFrame(candles)
    df.rename(columns={"datetime": "timestamp"}, inplace=True)
    df["datetime"] = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
    df["datetime"] = df["datetime"].dt.tz_convert("America/New_York")
    df["date"] = df["datetime"].dt.date.astype(str)

    return df


def get_price_history_raw(
    client: Client | AsyncClient,
    symbol: str,
    interval: SchwabPriceHistoryInterval | SchwabPriceHistoryIntervalStr,
    start_datetime: datetime | None = None,
    end_datetime: datetime | None = None,
    need_extended_hours_data: bool | None = None,
    need_previous_close: bool | None = None,
) -> dict:
    """
    Get price history from Schwab as raw dict.

    Args:
        client: Schwab API client
        symbol: Ticker symbol
        interval: Time interval (1, 5, 10, 15, 30 minutes, or "d", "w" for daily/weekly)
        start_datetime: Start of date range
        end_datetime: End of date range
        need_extended_hours_data: Include extended hours data (default True in API)
        need_previous_close: Include previous close price

    Returns:
        Raw dict from Schwab API
    """
    _warn_if_futures(symbol)  # Check before normalization strips the /
    symbol = normalize_ticker(symbol)
    method_name = _INTERVAL_TO_METHOD[interval]

    response = getattr(client, method_name)(
        symbol,
        start_datetime=start_datetime,
        end_datetime=end_datetime,
        need_extended_hours_data=need_extended_hours_data,
        need_previous_close=need_previous_close,
    )

    return response.json()


def get_price_history(
    client: Client | AsyncClient,
    symbol: str,
    interval: SchwabPriceHistoryInterval | SchwabPriceHistoryIntervalStr,
    start_datetime: datetime | None = None,
    end_datetime: datetime | None = None,
    need_extended_hours_data: bool | None = None,
    need_previous_close: bool | None = None,
    set_datetime_index: bool = True,
) -> pd.DataFrame:
    """
    Get price history from Schwab as a pandas DataFrame.

    Args:
        client: Schwab API client
        symbol: Ticker symbol
        interval: Time interval (1, 5, 10, 15, 30 minutes, or "d", "w" for daily/weekly)
        start_datetime: Start of date range
        end_datetime: End of date range
        need_extended_hours_data: Include extended hours data (default True in API)
        need_previous_close: Include previous close price
        set_datetime_index: If True, set datetime as the index

    Returns:
        DataFrame with OHLCV data
    """
    raw = get_price_history_raw(
        client,
        symbol,
        interval,
        start_datetime=start_datetime,
        end_datetime=end_datetime,
        need_extended_hours_data=need_extended_hours_data,
        need_previous_close=need_previous_close,
    )

    df = price_history_to_df(raw)

    if set_datetime_index and "datetime" in df.columns:
        df = df.set_index("datetime")

    return df


# Alias for backwards compatibility
get_price_history_from_schwab = get_price_history
get_price_history_from_schwab_non_df = get_price_history_raw
