"""K-line (OHLC bar) models."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

try:
    import pandas as pd

    _HAS_PANDAS = True
except ImportError:
    _HAS_PANDAS = False


@dataclass
class KlineBar:
    """A single OHLC bar (candlestick).

    Attributes
    ----------
    ts : int
        Bar timestamp in milliseconds (UTC epoch, aligned to the timeframe boundary).
    open : float
        Opening price.
    high : float
        Highest price within the bar period.
    low : float
        Lowest price within the bar period.
    close : float
        Closing price.
    """

    ts: int
    open: float
    high: float
    low: float
    close: float

    @property
    def mid(self) -> float:
        """Mid price = (high + low) / 2."""
        return (self.high + self.low) / 2.0

    @property
    def body_size(self) -> float:
        """Absolute difference between open and close."""
        return abs(self.close - self.open)

    @property
    def is_bullish(self) -> bool:
        """True if close >= open (green candle)."""
        return self.close >= self.open

    def to_dict(self) -> Dict[str, Any]:
        return {
            "ts": self.ts,
            "open": self.open,
            "high": self.high,
            "low": self.low,
            "close": self.close,
        }

    @classmethod
    def from_list(cls, lst: List) -> "KlineBar":
        """Construct from [ts, open, high, low, close] list (Flask server format)."""
        return cls(
            ts=int(lst[0]),
            open=float(lst[1]),
            high=float(lst[2]),
            low=float(lst[3]),
            close=float(lst[4]),
        )

    @classmethod
    def from_api(cls, data: Dict[str, Any]) -> "KlineBar":
        return cls(
            ts=int(data["ts"]),
            open=float(data["open"]),
            high=float(data["high"]),
            low=float(data["low"]),
            close=float(data["close"]),
        )


@dataclass
class KlineSeries:
    """A collection of K-line bars for a symbol.

    Attributes
    ----------
    symbol : str
        Product symbol.
    tf : str
        Timeframe string: "5min", "10min", "1hour".
    bars : List[KlineBar]
        Chronologically ordered bars (oldest first).
    """

    symbol: str
    tf: str
    bars: List[KlineBar] = field(default_factory=list)

    @classmethod
    def from_api(cls, payload: Dict[str, Any]) -> "KlineSeries":
        raw_bars = payload.get("bars", [])
        bars = [KlineBar.from_list(b) if isinstance(b, list) else KlineBar.from_api(b) for b in raw_bars]
        return cls(
            symbol=payload.get("symbol", ""),
            tf=payload.get("tf", "5min"),
            bars=bars,
        )

    def to_pandas(self) -> "pd.DataFrame":
        """Convert bars to a DataFrame with ts/open/high/low/close columns."""
        if not _HAS_PANDAS:
            raise ImportError("pandas is required for to_pandas()")
        rows = [bar.to_dict() for bar in self.bars]
        return pd.DataFrame(rows)

    def __len__(self) -> int:
        return len(self.bars)

    def __getitem__(self, index: int) -> KlineBar:
        return self.bars[index]
