"""
Data models for the DataSource interface.

Inspired by TradingView's Datafeed API but with flexible column schemas
for AI-native trading platform needs.
"""

from enum import StrEnum
from typing import Any, Dict, List, Literal, Optional

from pydantic import BaseModel, Field


class Resolution(StrEnum):
    """Standard time resolutions for bar data"""

    # Seconds
    S1 = "1S"
    S5 = "5S"
    S15 = "15S"
    S30 = "30S"

    # Minutes
    M1 = "1"
    M5 = "5"
    M15 = "15"
    M30 = "30"

    # Hours
    H1 = "60"
    H2 = "120"
    H4 = "240"
    H6 = "360"
    H12 = "720"

    # Days
    D1 = "1D"

    # Weeks
    W1 = "1W"

    # Months
    MO1 = "1M"


class ColumnInfo(BaseModel):
    """
    Metadata for a single data column.

    Provides rich, LLM-readable descriptions so AI agents can understand
    and reason about available data fields.
    """

    model_config = {"extra": "forbid"}

    name: str = Field(description="Column name (e.g., 'close', 'volume', 'funding_rate')")
    type: Literal["float", "int", "bool", "string", "decimal"] = Field(description="Data type")
    description: str = Field(description="Human and LLM-readable description of what this column represents")
    unit: Optional[str] = Field(default=None, description="Unit of measurement (e.g., 'USD', 'BTC', '%', 'contracts')")
    nullable: bool = Field(default=False, description="Whether this column can contain null values")


class SymbolInfo(BaseModel):
    """
    Complete metadata for a tradeable symbol.

    Includes both TradingView-compatible fields and flexible schema definition
    for arbitrary data columns.
    """

    model_config = {"extra": "forbid"}

    # Core identification
    symbol: str = Field(description="Unique symbol identifier (primary key for data fetching)")
    ticker: Optional[str] = Field(default=None, description="TradingView ticker (if different from symbol)")
    name: str = Field(description="Display name")
    description: str = Field(description="LLM-readable description of the instrument")
    type: str = Field(description="Instrument type: 'crypto', 'stock', 'forex', 'futures', 'derived', etc.")
    exchange: str = Field(description="Exchange or data source identifier")

    # Trading session info
    timezone: str = Field(default="Etc/UTC", description="IANA timezone identifier")
    session: str = Field(default="24x7", description="Trading session spec (e.g., '0930-1600' or '24x7')")

    # Resolution support
    supported_resolutions: List[str] = Field(description="List of supported time resolutions")
    has_intraday: bool = Field(default=True, description="Whether intraday resolutions are supported")
    has_daily: bool = Field(default=True, description="Whether daily resolution is supported")
    has_weekly_and_monthly: bool = Field(default=False, description="Whether weekly/monthly resolutions are supported")

    # Flexible schema definition
    columns: List[ColumnInfo] = Field(description="Available data columns for this symbol")
    time_column: str = Field(default="time", description="Name of the timestamp column")

    # Convenience flags
    has_ohlcv: bool = Field(default=False, description="Whether standard OHLCV columns are present")

    # Price display (for OHLCV data)
    pricescale: int = Field(default=100, description="Price scale factor (e.g., 100 for 2 decimals)")
    minmov: int = Field(default=1, description="Minimum price movement in pricescale units")

    # Additional metadata
    base_currency: Optional[str] = Field(default=None, description="Base currency (for crypto/forex)")
    quote_currency: Optional[str] = Field(default=None, description="Quote currency (for crypto/forex)")


class Bar(BaseModel):
    """
    A single bar/row of time-series data with flexible columns.

    All bars must have a timestamp. Additional columns are stored in the
    data dict and described by the associated ColumnInfo metadata.
    """

    model_config = {"extra": "forbid"}

    time: int = Field(description="Unix timestamp in seconds")
    data: Dict[str, Any] = Field(description="Column name -> value mapping")

    # Convenience accessors for common OHLCV columns
    @property
    def open(self) -> Optional[float]:
        return self.data.get("open")

    @property
    def high(self) -> Optional[float]:
        return self.data.get("high")

    @property
    def low(self) -> Optional[float]:
        return self.data.get("low")

    @property
    def close(self) -> Optional[float]:
        return self.data.get("close")

    @property
    def volume(self) -> Optional[float]:
        return self.data.get("volume")


class HistoryResult(BaseModel):
    """
    Result from a historical data query.

    Includes the bars, schema information, and pagination metadata.
    """

    model_config = {"extra": "forbid"}

    symbol: str = Field(description="Symbol identifier")
    resolution: str = Field(description="Time resolution of the bars")
    bars: List[Bar] = Field(description="The actual data bars")
    columns: List[ColumnInfo] = Field(description="Schema describing the bar data columns")
    nextTime: Optional[int] = Field(default=None, description="Unix timestamp for pagination (if more data available)")


class SearchResult(BaseModel):
    """
    A single result from symbol search.
    """

    model_config = {"extra": "forbid"}

    symbol: str = Field(description="Display symbol (e.g., 'BINANCE:ETH/BTC')")
    ticker: Optional[str] = Field(default=None, description="Backend ticker for data fetching (e.g., 'ETH/BTC')")
    full_name: str = Field(description="Full display name including exchange")
    description: str = Field(description="Human-readable description")
    exchange: str = Field(description="Exchange identifier")
    type: str = Field(description="Instrument type")


class DatafeedConfig(BaseModel):
    """
    Configuration and capabilities of a DataSource.

    Similar to TradingView's onReady configuration object.
    """

    model_config = {"extra": "forbid"}

    # Supported features
    supported_resolutions: List[str] = Field(description="All resolutions this datafeed supports")
    supports_search: bool = Field(default=True, description="Whether symbol search is available")
    supports_time: bool = Field(default=True, description="Whether time-based queries are supported")
    supports_marks: bool = Field(default=False, description="Whether marks/events are supported")

    # Data characteristics
    exchanges: List[str] = Field(default_factory=list, description="Available exchanges")
    symbols_types: List[str] = Field(default_factory=list, description="Available instrument types")

    # Metadata
    name: str = Field(description="Datafeed name")
    description: str = Field(description="LLM-readable description of this data source")
