"""Pydantic models for universal MT5 operations."""

from enum import Enum
from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field, field_validator


class MT5Operation(str, Enum):
    """Available MT5 read-only operations."""

    # Market data operations
    COPY_RATES_FROM = "copy_rates_from"
    COPY_RATES_FROM_POS = "copy_rates_from_pos"
    COPY_RATES_RANGE = "copy_rates_range"
    COPY_TICKS_FROM = "copy_ticks_from"
    COPY_TICKS_RANGE = "copy_ticks_range"

    # Symbol operations
    SYMBOL_INFO = "symbol_info"
    SYMBOL_INFO_TICK = "symbol_info_tick"
    SYMBOL_SELECT = "symbol_select"
    SYMBOLS_TOTAL = "symbols_total"
    SYMBOLS_GET = "symbols_get"

    # Account/Terminal info
    ACCOUNT_INFO = "account_info"
    TERMINAL_INFO = "terminal_info"
    VERSION = "version"

    # Calculation operations
    ORDER_CALC_MARGIN = "order_calc_margin"
    ORDER_CALC_PROFIT = "order_calc_profit"


class TimeFrame(str, Enum):
    """MT5 timeframe constants."""

    M1 = "M1"
    M2 = "M2"
    M3 = "M3"
    M4 = "M4"
    M5 = "M5"
    M6 = "M6"
    M10 = "M10"
    M12 = "M12"
    M15 = "M15"
    M20 = "M20"
    M30 = "M30"
    H1 = "H1"
    H2 = "H2"
    H3 = "H3"
    H4 = "H4"
    H6 = "H6"
    H8 = "H8"
    H12 = "H12"
    D1 = "D1"
    W1 = "W1"
    MN1 = "MN1"


# ============================================================================
# REQUEST MODELS
# ============================================================================


class MT5QueryRequest(BaseModel):
    """Universal MT5 query request supporting all read-only operations."""

    operation: MT5Operation = Field(..., description="MT5 operation to execute")

    symbol: Optional[str] = Field(
        None,
        description="Trading symbol (required for symbol-specific operations)",
        min_length=3,
        max_length=20,
    )

    parameters: Dict[str, Any] = Field(
        default_factory=dict,
        description="Operation-specific parameters (e.g., timeframe, count, start_pos, dates)",
    )

    @field_validator("symbol")
    @classmethod
    def validate_symbol(cls, v: Optional[str]) -> Optional[str]:
        """Validate and normalize symbol."""
        if v:
            return v.strip().upper()
        return v

    class Config:
        json_schema_extra = {
            "examples": [
                {
                    "operation": "copy_rates_from_pos",
                    "symbol": "BTCUSD",
                    "parameters": {"timeframe": "H1", "start_pos": 0, "count": 100},
                },
                {"operation": "symbol_info", "symbol": "EURUSD", "parameters": {}},
                {"operation": "symbols_get", "parameters": {"group": "*USD*"}},
            ]
        }


class IndicatorSpec(BaseModel):
    """Specification for a technical indicator."""

    function: str = Field(
        ...,
        description="TA-Lib function path (e.g., 'ta.momentum.rsi', 'ta.trend.sma_indicator')",
        pattern=r"^ta\.\w+\.\w+$",
    )

    column_name: Optional[str] = Field(
        None, description="Output column name (auto-generated if not provided)"
    )

    params: Dict[str, Any] = Field(
        default_factory=dict,
        description="Indicator parameters (e.g., {'window': 14} for RSI)",
    )

    class Config:
        json_schema_extra = {
            "examples": [
                {"function": "ta.momentum.rsi", "params": {"window": 14}},
                {"function": "ta.trend.sma_indicator", "params": {"window": 20}},
                {"function": "ta.trend.ema_indicator", "params": {"window": 50}},
                {"function": "ta.volatility.bollinger_hband", "params": {"window": 20}},
                {"function": "ta.trend.macd", "params": {}},
            ]
        }


class ChartPanel(BaseModel):
    """Configuration for a chart panel."""

    columns: List[str] = Field(..., description="Column names to plot in this panel", min_length=1)

    style: str = Field(default="line", description="Plot style: 'line', 'scatter', 'bar'")

    y_label: Optional[str] = Field(None, description="Y-axis label")

    y_limits: Optional[List[float]] = Field(
        None, description="Y-axis limits [min, max]", min_length=2, max_length=2
    )

    reference_lines: Optional[List[float]] = Field(
        None, description="Horizontal reference lines (e.g., [30, 70] for RSI)"
    )


class ChartConfig(BaseModel):
    """Configuration for chart generation."""

    type: str = Field(
        default="single",
        description="Chart type: 'single' (one panel) or 'multi' (multiple panels)",
    )

    panels: List[ChartPanel] = Field(
        default_factory=lambda: [ChartPanel(columns=["close"])],
        description="Panel configurations (one for single, multiple for multi)",
        min_length=1,
        max_length=5,
    )

    title: Optional[str] = Field(None, description="Chart title (auto-generated if not provided)")

    filename: str = Field(
        default="chart.png", description="Output filename", pattern=r"^[\w\-]+\.png$"
    )

    width: int = Field(default=14, ge=6, le=24, description="Width in inches")
    height: int = Field(default=8, ge=4, le=20, description="Height in inches")
    dpi: int = Field(default=120, ge=50, le=300, description="Resolution")


class ForecastConfig(BaseModel):
    """Configuration for Prophet time series forecasting."""

    periods: int = Field(
        default=30,
        description="Number of periods to forecast into the future",
        ge=1,
        le=365,
    )

    include_history: bool = Field(
        default=False, description="Include historical fitted values in forecast output"
    )

    freq: Optional[str] = Field(
        None,
        description=(
            "Forecast frequency (auto-detected from data if not specified): "
            "'D' (daily), 'h' (hourly), 'min' (minutely)"
        ),
    )

    uncertainty_samples: int = Field(
        default=1000,
        description="Number of samples for uncertainty intervals",
        ge=0,
        le=10000,
    )

    seasonality_mode: str = Field(
        default="additive",
        description="Seasonality mode: 'additive' or 'multiplicative'",
    )

    growth: str = Field(default="linear", description="Growth model: 'linear' or 'logistic'")

    plot: bool = Field(default=True, description="Generate forecast visualization chart")

    plot_components: bool = Field(
        default=False,
        description="Generate components plot (trend, seasonality, holidays)",
    )

    enable_ml_prediction: bool = Field(
        default=False,
        description="Enable XGBoost ML model for buy/sell signal prediction",
    )

    ml_lookback: int = Field(
        default=50,
        description="Number of bars to use for ML feature engineering",
        ge=20,
        le=200,
    )

    @field_validator("seasonality_mode")
    @classmethod
    def validate_seasonality_mode(cls, v: str) -> str:
        """Validate seasonality mode."""
        v = v.lower()
        if v not in ["additive", "multiplicative"]:
            raise ValueError("seasonality_mode must be 'additive' or 'multiplicative'")
        return v

    @field_validator("growth")
    @classmethod
    def validate_growth(cls, v: str) -> str:
        """Validate growth model."""
        v = v.lower()
        if v not in ["linear", "logistic"]:
            raise ValueError("growth must be 'linear' or 'logistic'")
        return v


class MT5AnalysisRequest(BaseModel):
    """Universal analysis request: query + analyze + visualize."""

    query: MT5QueryRequest = Field(..., description="MT5 data query configuration")

    indicators: Optional[List[IndicatorSpec]] = Field(
        None, description="Technical indicators to calculate", max_length=20
    )

    chart: Optional[ChartConfig] = Field(
        None, description="Chart generation configuration (omit for data-only response)"
    )

    forecast: Optional[ForecastConfig] = Field(
        None, description="Prophet forecast configuration (omit to skip forecasting)"
    )

    output_format: str = Field(
        default="markdown",
        description="Data output format: 'markdown', 'json', 'chart_only'",
    )

    tail: Optional[int] = Field(
        None,
        description="Return only last N rows of data (None = all rows)",
        ge=1,
        le=1000,
    )

    @field_validator("output_format")
    @classmethod
    def validate_output_format(cls, v: str) -> str:
        """Validate output format."""
        v = v.lower()
        if v not in ["markdown", "json", "chart_only"]:
            raise ValueError("output_format must be 'markdown', 'json', or 'chart_only'")
        return v

    class Config:
        json_schema_extra = {
            "examples": [
                {
                    "query": {
                        "operation": "copy_rates_from_pos",
                        "symbol": "BTCUSD",
                        "parameters": {"timeframe": "D1", "start_pos": 0, "count": 30},
                    },
                    "indicators": [
                        {"function": "ta.momentum.rsi", "params": {"window": 14}},
                        {
                            "function": "ta.trend.sma_indicator",
                            "params": {"window": 20},
                        },
                    ],
                    "chart": {
                        "type": "multi",
                        "panels": [
                            {"columns": ["close", "sma_indicator_20"], "style": "line"},
                            {
                                "columns": ["rsi"],
                                "style": "line",
                                "y_limits": [0, 100],
                                "reference_lines": [30, 70],
                            },
                        ],
                        "filename": "btcusd_trend.png",
                    },
                    "output_format": "chart_only",
                }
            ]
        }


# ============================================================================
# RESPONSE MODELS
# ============================================================================


class MT5QueryResponse(BaseModel):
    """Universal query response."""

    operation: str
    success: bool
    data: Any
    metadata: Dict[str, Any] = Field(default_factory=dict)


class MT5AnalysisResponse(BaseModel):
    """Universal analysis response."""

    success: bool
    data: Optional[Any] = None
    chart_path: Optional[str] = None
    forecast_chart_path: Optional[str] = None
    indicators_calculated: List[str] = Field(default_factory=list)
    metadata: Dict[str, Any] = Field(default_factory=dict)


class ErrorResponse(BaseModel):
    """Error response with suggestions."""

    error: str
    error_type: str
    suggestion: Optional[str] = None
    corrected_params: Optional[Dict[str, Any]] = None
    example: Optional[Dict[str, Any]] = None
