"""Parameter optimization based on trades analysis."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

import polars as pl

if TYPE_CHECKING:
    import plotly.graph_objects as go

    from ..mae_mfe.config import ChartConfig


@dataclass
class OptimalExitParameters:
    """Suggested exit parameters based on MAE/MFE distribution analysis.

    These values are derived from statistical analysis of trade excursions
    and should be validated through backtesting before live use.
    """

    # Stop Loss (based on MAE distribution)
    sl_p50_raw: float  # 50th percentile MAE in raw price units
    sl_p75_raw: float  # 75th percentile MAE in raw price units
    sl_p50_atr: float | None  # 50th percentile MAE in ATR units
    sl_p75_atr: float | None  # 75th percentile MAE in ATR units

    # Take Profit (based on MFE/G_MFE distribution)
    tp_p50_raw: float  # 50th percentile MFE in raw price units
    tp_p75_raw: float  # 75th percentile MFE in raw price units
    tp_p50_atr: float | None  # 50th percentile MFE in ATR units
    tp_p75_atr: float | None  # 75th percentile MFE in ATR units

    # Trailing Stop (based on MHL distribution)
    trailing_activation_raw: float  # Suggested profit before activating trail
    trailing_distance_raw: float  # Suggested trailing distance
    trailing_activation_atr: float | None
    trailing_distance_atr: float | None

    # Breakeven
    breakeven_activation_raw: float  # Suggested profit before moving to BE
    breakeven_activation_atr: float | None

    # Timeout
    optimal_timeout_bars: int  # Suggested timeout based on holding period

    # Confidence metrics
    sample_size: int
    sl_hit_rate: float  # Proportion of trades that hit calculated SL
    tp_hit_rate: float  # Proportion of trades that hit calculated TP

    def summary(self) -> str:
        """Return formatted summary string."""
        lines = [
            "=== Optimal Exit Parameters ===",
            f"Sample size: {self.sample_size} trades",
            "",
            "Stop Loss:",
            f"  P50: {self.sl_p50_raw:.4f} raw",
            f"  P75: {self.sl_p75_raw:.4f} raw",
        ]
        if self.sl_p50_atr is not None:
            lines.extend(
                [
                    f"  P50: {self.sl_p50_atr:.2f} ATR",
                    f"  P75: {self.sl_p75_atr:.2f} ATR",
                ]
            )
        lines.append(f"  Hit rate @ P75: {self.sl_hit_rate:.1%}")

        lines.extend(
            [
                "",
                "Take Profit:",
                f"  P50: {self.tp_p50_raw:.4f} raw",
                f"  P75: {self.tp_p75_raw:.4f} raw",
            ]
        )
        if self.tp_p50_atr is not None:
            lines.extend(
                [
                    f"  P50: {self.tp_p50_atr:.2f} ATR",
                    f"  P75: {self.tp_p75_atr:.2f} ATR",
                ]
            )
        lines.append(f"  Hit rate @ P50: {self.tp_hit_rate:.1%}")

        lines.extend(
            [
                "",
                "Trailing Stop:",
                f"  Activation: {self.trailing_activation_raw:.4f} raw",
                f"  Distance: {self.trailing_distance_raw:.4f} raw",
            ]
        )
        if self.trailing_activation_atr is not None:
            lines.extend(
                [
                    f"  Activation: {self.trailing_activation_atr:.2f} ATR",
                    f"  Distance: {self.trailing_distance_atr:.2f} ATR",
                ]
            )

        lines.extend(
            [
                "",
                "Breakeven:",
                f"  Activation: {self.breakeven_activation_raw:.4f} raw",
            ]
        )
        if self.breakeven_activation_atr is not None:
            lines.append(f"  Activation: {self.breakeven_activation_atr:.2f} ATR")

        lines.extend(
            [
                "",
                f"Timeout: {self.optimal_timeout_bars} bars",
            ]
        )

        return "\n".join(lines)


def calculate_optimal_parameters(df: pl.DataFrame) -> OptimalExitParameters:
    """Calculate optimal exit parameters based on MAE/MFE distribution.

    Args:
        df: Trades DataFrame with MAE/MFE columns.

    Returns:
        OptimalExitParameters with suggested values.

    Notes:
        - SL recommendations are based on MAE (maximum adverse excursion)
        - TP recommendations are based on MFE (maximum favorable excursion)
        - ATR-normalized values require volatility columns
    """
    n_trades = len(df)
    if n_trades == 0:
        raise ValueError("Cannot calculate parameters from empty DataFrame")

    # MAE analysis (Stop Loss)
    has_mae = "mae" in df.columns
    has_mae_vol = "mae_volatility" in df.columns

    if has_mae:
        mae_p50 = df.select(pl.col("mae").quantile(0.5))["mae"][0]
        mae_p75 = df.select(pl.col("mae").quantile(0.75))["mae"][0]
        sl_hit_rate = df.filter(pl.col("mae") <= mae_p75).height / n_trades
    else:
        mae_p50 = 0.0
        mae_p75 = 0.0
        sl_hit_rate = 0.0

    mae_atr_p50 = None
    mae_atr_p75 = None
    if has_mae and has_mae_vol:
        mae_atr = df.select((pl.col("mae") / pl.col("mae_volatility")).alias("mae_atr"))["mae_atr"]
        mae_atr_p50 = mae_atr.quantile(0.5)
        mae_atr_p75 = mae_atr.quantile(0.75)

    # MFE analysis (Take Profit) - prefer g_mfe if available
    mfe_col = "g_mfe" if "g_mfe" in df.columns else "mfe"
    mfe_vol_col = (
        "g_mfe_volatility"
        if "g_mfe_volatility" in df.columns
        else ("mfe_volatility" if "mfe_volatility" in df.columns else None)
    )

    has_mfe = mfe_col in df.columns

    if has_mfe:
        mfe_p50 = df.select(pl.col(mfe_col).quantile(0.5))[mfe_col][0]
        mfe_p75 = df.select(pl.col(mfe_col).quantile(0.75))[mfe_col][0]
        tp_hit_rate = df.filter(pl.col(mfe_col) >= mfe_p50).height / n_trades
    else:
        mfe_p50 = 0.0
        mfe_p75 = 0.0
        tp_hit_rate = 0.0

    mfe_atr_p50 = None
    mfe_atr_p75 = None
    if has_mfe and mfe_vol_col and mfe_vol_col in df.columns:
        mfe_atr = df.select((pl.col(mfe_col) / pl.col(mfe_vol_col)).alias("mfe_atr"))["mfe_atr"]
        mfe_atr_p50 = mfe_atr.quantile(0.5)
        mfe_atr_p75 = mfe_atr.quantile(0.75)

    # MHL analysis (Trailing Stop)
    has_mhl = "mhl" in df.columns
    has_mhl_vol = "mhl_volatility" in df.columns

    if has_mhl and has_mfe:
        # Activation: when trade reaches ~50% of typical MFE
        trailing_activation = mfe_p50 * 0.5
        # Distance: based on MHL distribution (pullback from high)
        mhl_p50 = df.select(pl.col("mhl").quantile(0.5))["mhl"][0]
        trailing_distance = mhl_p50
    else:
        trailing_activation = mfe_p50 * 0.5 if mfe_p50 > 0 else 0.0
        trailing_distance = mae_p50 * 0.5 if mae_p50 > 0 else 0.0

    trailing_activation_atr = None
    trailing_distance_atr = None
    if has_mfe and mfe_atr_p50 is not None:
        trailing_activation_atr = mfe_atr_p50 * 0.5
    if has_mhl and has_mhl_vol:
        mhl_atr = df.select((pl.col("mhl") / pl.col("mhl_volatility")).alias("mhl_atr"))["mhl_atr"]
        trailing_distance_atr = mhl_atr.quantile(0.5)

    # Breakeven activation
    breakeven_activation = trailing_activation  # Same as trailing activation
    breakeven_activation_atr = trailing_activation_atr

    # Timeout (based on holding period)
    if "holding_bars" in df.columns:
        # Use 75th percentile of holding bars as timeout
        timeout_bars = int(df.select(pl.col("holding_bars").quantile(0.75))["holding_bars"][0])
    else:
        timeout_bars = 50  # Default fallback

    return OptimalExitParameters(
        sl_p50_raw=mae_p50,
        sl_p75_raw=mae_p75,
        sl_p50_atr=mae_atr_p50,
        sl_p75_atr=mae_atr_p75,
        tp_p50_raw=mfe_p50,
        tp_p75_raw=mfe_p75,
        tp_p50_atr=mfe_atr_p50,
        tp_p75_atr=mfe_atr_p75,
        trailing_activation_raw=trailing_activation,
        trailing_distance_raw=trailing_distance,
        trailing_activation_atr=trailing_activation_atr,
        trailing_distance_atr=trailing_distance_atr,
        breakeven_activation_raw=breakeven_activation,
        breakeven_activation_atr=breakeven_activation_atr,
        optimal_timeout_bars=timeout_bars,
        sample_size=n_trades,
        sl_hit_rate=sl_hit_rate,
        tp_hit_rate=tp_hit_rate,
    )


def create_optimization_dashboard(
    df: pl.DataFrame,
    config: ChartConfig,
) -> go.Figure:
    """Create a dashboard showing parameter optimization insights.

    Args:
        df: Trades DataFrame.
        config: Chart configuration.

    Returns:
        Plotly Figure with optimization visualizations.
    """
    try:
        import plotly.graph_objects as go
        from plotly.subplots import make_subplots
    except ImportError as e:
        raise ImportError(
            "Plotly is required for optimization dashboard. Install with: pip install plotly>=6.3.1"
        ) from e

    params = calculate_optimal_parameters(df)

    fig = make_subplots(
        rows=2,
        cols=2,
        subplot_titles=(
            "MAE Distribution (Stop Loss)",
            "MFE Distribution (Take Profit)",
            "Holding Period Distribution",
            "Parameter Summary",
        ),
        specs=[
            [{"type": "histogram"}, {"type": "histogram"}],
            [{"type": "histogram"}, {"type": "table"}],
        ],
    )

    # MAE histogram
    if "mae" in df.columns:
        mae_values = df["mae"].to_list()
        fig.add_trace(
            go.Histogram(
                x=mae_values,
                name="MAE",
                marker_color=config.color_loser,
                opacity=0.7,
                nbinsx=50,
            ),
            row=1,
            col=1,
        )
        # Add P75 line
        fig.add_vline(
            x=params.sl_p75_raw,
            line_dash="dash",
            line_color="white",
            annotation_text=f"P75: {params.sl_p75_raw:.4f}",
            row=1,
            col=1,
        )

    # MFE histogram
    mfe_col = "g_mfe" if "g_mfe" in df.columns else "mfe"
    if mfe_col in df.columns:
        mfe_values = df[mfe_col].to_list()
        fig.add_trace(
            go.Histogram(
                x=mfe_values,
                name="MFE",
                marker_color=config.color_winner,
                opacity=0.7,
                nbinsx=50,
            ),
            row=1,
            col=2,
        )
        # Add P50 line
        fig.add_vline(
            x=params.tp_p50_raw,
            line_dash="dash",
            line_color="white",
            annotation_text=f"P50: {params.tp_p50_raw:.4f}",
            row=1,
            col=2,
        )

    # Holding period histogram
    if "holding_bars" in df.columns:
        holding_values = df["holding_bars"].to_list()
        fig.add_trace(
            go.Histogram(
                x=holding_values,
                name="Holding Bars",
                marker_color=config.color_neutral,
                opacity=0.7,
                nbinsx=50,
            ),
            row=2,
            col=1,
        )
        # Add timeout line
        fig.add_vline(
            x=params.optimal_timeout_bars,
            line_dash="dash",
            line_color="white",
            annotation_text=f"Timeout: {params.optimal_timeout_bars}",
            row=2,
            col=1,
        )

    # Summary table
    summary_data = [
        ["Parameter", "Raw", "ATR"],
        [
            "SL P75",
            f"{params.sl_p75_raw:.4f}",
            f"{params.sl_p75_atr:.2f}" if params.sl_p75_atr else "N/A",
        ],
        [
            "TP P50",
            f"{params.tp_p50_raw:.4f}",
            f"{params.tp_p50_atr:.2f}" if params.tp_p50_atr else "N/A",
        ],
        [
            "Trail Activation",
            f"{params.trailing_activation_raw:.4f}",
            f"{params.trailing_activation_atr:.2f}" if params.trailing_activation_atr else "N/A",
        ],
        [
            "Trail Distance",
            f"{params.trailing_distance_raw:.4f}",
            f"{params.trailing_distance_atr:.2f}" if params.trailing_distance_atr else "N/A",
        ],
        ["Timeout", f"{params.optimal_timeout_bars} bars", "-"],
    ]

    fig.add_trace(
        go.Table(
            header=dict(
                values=summary_data[0],
                fill_color="paleturquoise",
                align="left",
            ),
            cells=dict(
                values=list(zip(*summary_data[1:])),
                fill_color="lavender",
                align="left",
            ),
        ),
        row=2,
        col=2,
    )

    fig.update_layout(
        title="Parameter Optimization Dashboard",
        template=config.theme,
        width=config.width,
        height=config.height * 1.5,
        showlegend=False,
    )

    return fig
