"""
Benchmarking module for comparing strategies.

This module provides utilities to run multiple strategies on the same data
and compare their performance.
"""

from pathlib import Path
from typing import Any

import pandas as pd

from jbqlab.engine import run_backtest
from jbqlab.plotting import plot_equity_curve
from jbqlab.report import generate_comparison_report
from jbqlab.strategies import get_available_strategies
from jbqlab.types import BacktestResult


def run_benchmark(
    data: str | Path | pd.DataFrame,
    strategies: list[dict[str, Any]] | None = None,
    transaction_cost: float = 0.001,
    slippage: float = 0.0005,
    initial_capital: float = 100_000.0,
) -> dict[str, BacktestResult]:
    """Run multiple strategies on the same data for comparison.

    Args:
        data: Path to CSV file or DataFrame with price data.
        strategies: List of strategy configurations. Each dict should have:
            - "name": strategy name (required)
            - Any strategy-specific parameters
            If None, runs all available strategies with default parameters.
        transaction_cost: Transaction cost for all backtests.
        slippage: Slippage for all backtests.
        initial_capital: Initial capital for all backtests.

    Returns:
        Dictionary mapping strategy names to BacktestResult objects.

    Example:
        >>> results = run_benchmark(
        ...     "data.csv",
        ...     strategies=[
        ...         {"name": "buy_and_hold"},
        ...         {"name": "sma_crossover", "fast": 10, "slow": 30},
        ...         {"name": "sma_crossover", "fast": 5, "slow": 20},
        ...     ]
        ... )
    """
    if strategies is None:
        # Default: run all strategies with default params
        available = get_available_strategies()
        strategies = [{"name": s.name} for s in available]

    results: dict[str, BacktestResult] = {}

    for config in strategies:
        name = config.pop("name")
        params = config

        # Create unique label if same strategy with different params
        label = f"{name}({','.join(f'{k}={v}' for k, v in params.items())})" if params else name

        try:
            result = run_backtest(
                data=data,
                strategy=name,
                transaction_cost=transaction_cost,
                slippage=slippage,
                initial_capital=initial_capital,
                **params,
            )
            results[label] = result
        except Exception as e:
            print(f"Warning: Strategy '{label}' failed: {e}")
            continue

        # Restore name for potential reuse
        config["name"] = name

    return results


def benchmark_summary(results: dict[str, BacktestResult]) -> pd.DataFrame:
    """Create a summary DataFrame comparing strategy results.

    Args:
        results: Dictionary mapping strategy names to BacktestResult objects.

    Returns:
        DataFrame with strategies as rows and metrics as columns.
    """
    data = []
    for name, result in results.items():
        row = {"strategy": name, **result.metrics}
        data.append(row)

    df = pd.DataFrame(data)

    # Sort by Sharpe ratio descending
    if "sharpe" in df.columns:
        df = df.sort_values("sharpe", ascending=False)

    return df.reset_index(drop=True)


def save_benchmark(
    results: dict[str, BacktestResult],
    output_dir: str | Path,
    generate_plots: bool = True,
) -> None:
    """Save benchmark results to files.

    Creates:
    - summary.csv: Comparison of all strategies
    - comparison_report.md: Markdown comparison report
    - {strategy}_equity.png: Equity curve for each strategy (optional)

    Args:
        results: Dictionary mapping strategy names to BacktestResult objects.
        output_dir: Directory to save results.
        generate_plots: Whether to generate equity curve plots.
    """
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    # Save summary CSV
    summary = benchmark_summary(results)
    summary.to_csv(output_dir / "summary.csv", index=False)

    # Generate comparison report
    generate_comparison_report(results, output_dir / "comparison_report.md")

    # Generate plots
    if generate_plots:
        for name, result in results.items():
            # Sanitize filename
            safe_name = name.replace("(", "_").replace(")", "").replace(",", "_").replace("=", "")
            plot_equity_curve(result, output_dir / f"{safe_name}_equity.png")

    print(f"✅ Benchmark results saved to {output_dir}/")


def print_benchmark_summary(results: dict[str, BacktestResult]) -> None:
    """Print a formatted benchmark summary to console.

    Args:
        results: Dictionary mapping strategy names to BacktestResult objects.
    """
    summary = benchmark_summary(results)

    print("\n" + "=" * 80)
    print("📊 BENCHMARK SUMMARY")
    print("=" * 80 + "\n")

    # Header
    print(f"{'Strategy':<35} {'Return':>10} {'Sharpe':>8} {'MaxDD':>8} {'Calmar':>8}")
    print("-" * 80)

    for _, row in summary.iterrows():
        name = row["strategy"][:33]
        ret = row.get("total_return", 0) * 100
        sharpe = row.get("sharpe", 0)
        max_dd = row.get("max_drawdown", 0) * 100
        calmar = row.get("calmar", 0)

        print(f"{name:<35} {ret:>9.2f}% {sharpe:>8.2f} {max_dd:>7.2f}% {calmar:>8.2f}")

    print("-" * 80)

    # Best performers
    best_sharpe = summary.iloc[0]["strategy"]
    best_return_idx = summary["total_return"].idxmax()
    best_return = summary.loc[best_return_idx, "strategy"]

    print(f"\n🏆 Best Risk-Adjusted (Sharpe): {best_sharpe}")
    print(f"🏆 Best Total Return: {best_return}")
    print()
