from __future__ import annotations

from typing import Any

from conftest import make_market_data

from signalab.backtest_engine import BacktestEngine
from signalab.experiment.param_grid import ParamGridBuilder
from signalab.experiment.runner import ExperimentResult, ExperimentRunner
from signalab.indicator.rsi import RSI
from signalab.strategy.base import Strategy


class _NoOpStrategy(Strategy):
    def __init__(self, lot: float = 0.01, label: str = "noop") -> None:
        super().__init__(name="NoOp", label=label)
        self.lot = lot

    def perform(self, current_market: Any) -> None:
        return None


class TestExperimentRunner:
    def test_run_executes_all_grid_configs(self) -> None:
        grid = (
            ParamGridBuilder()
            .strategy(_NoOpStrategy, lot=[0.01, 0.02], label=["s1"])
            .indicator(
                RSI, period=[7, 14], buy_level=[35], sell_level=[65], label=["rsi"]
            )
            .account(initial_balance=[5_000], leverage=[100])
            .build()
        )

        runner = ExperimentRunner(
            data=make_market_data(n=120, freq="1min"),
            backtest_engine=BacktestEngine(verbose=False),
        )

        result = runner.run(grid)

        assert isinstance(result, ExperimentResult)
        assert len(result) == len(grid)
        assert all(run.report.indicators_df is not None for run in result.runs)

    def test_summary_returns_dataframe(self) -> None:
        grid = (
            ParamGridBuilder()
            .strategy(_NoOpStrategy, lot=[0.01], label=["s1"])
            .account(initial_balance=[10_000], leverage=[100])
            .build()
        )

        runner = ExperimentRunner(
            data=make_market_data(n=50, freq="1min"),
            backtest_engine=BacktestEngine(verbose=False),
        )

        result = runner.run(grid)
        summary = result.summary()

        assert len(summary) == 1
        assert "run_id" in summary.columns
        assert "strategy_cls" in summary.columns
        assert "total_return" in summary.columns

    def test_run_parallel_with_n_jobs(self) -> None:
        grid = (
            ParamGridBuilder()
            .strategy(_NoOpStrategy, lot=[0.01, 0.02], label=["s1"])
            .account(initial_balance=[10_000], leverage=[100])
            .build()
        )

        runner = ExperimentRunner(
            data=make_market_data(n=80, freq="1min"),
            backtest_engine=BacktestEngine(verbose=False),
        )

        result = runner.run(grid, n_jobs=2)

        assert len(result) == len(grid)
