from __future__ import annotations

from typing import Any

import pytest

from signalab.experiment.param_grid import ParamGridBuilder
from signalab.indicator.base import Indicator
from signalab.indicator.rsi import RSI
from signalab.strategy.base import Strategy


class _TestStrategy(Strategy):
    def __init__(self, lot: float = 0.01, label: str = "test_strategy") -> None:
        super().__init__(name="TestStrategy", label=label)
        self.lot = lot

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


class _TestIndicator(Indicator):
    def __init__(self, period: int = 5, label: str = "test_indicator") -> None:
        super().__init__(name="TestIndicator", label=label)
        self.period = period

    def compute(self, data: Any) -> Any:
        return data


class TestParamGridBuilder:
    def test_single_strategy_single_indicator_build(self) -> None:
        grid = (
            ParamGridBuilder()
            .strategy(_TestStrategy, lot=[0.01, 0.02])
            .indicator(_TestIndicator, period=[5, 10])
            .account(initial_balance=[5_000, 10_000], leverage=[100])
            .build()
        )

        assert len(grid) == 8

    def test_strategy_scalar_params_are_promoted(self) -> None:
        grid = ParamGridBuilder().strategy(_TestStrategy, lot=0.01).build()
        configs = grid.to_list()
        assert len(configs) == 1
        assert configs[0].strategy_params["lot"] == 0.01

    def test_indicator_selection_k_of_n(self) -> None:
        grid = (
            ParamGridBuilder()
            .strategy(_TestStrategy, lot=[0.01])
            .indicator(_TestIndicator, period=[5])
            .indicator(RSI, period=[7], buy_level=[35], sell_level=[65], label=["rsi"])
            .indicator_selection_k_of_n(1)
            .build()
        )

        assert len(grid) == 2
        sizes = [len(config.indicators) for config in grid]
        assert sizes == [1, 1]

    def test_indicator_selection_all_subsets(self) -> None:
        grid = (
            ParamGridBuilder()
            .strategy(_TestStrategy, lot=[0.01])
            .indicator(_TestIndicator, period=[5])
            .indicator(RSI, period=[7], buy_level=[35], sell_level=[65], label=["rsi"])
            .indicator_selection_all_subsets(include_empty=False)
            .build()
        )

        # C(2,1) + C(2,2) = 3 subsets
        assert len(grid) == 3

    def test_invalid_strategy_param_raises(self) -> None:
        with pytest.raises(ValueError, match="Unknown strategy parameter"):
            ParamGridBuilder().strategy(_TestStrategy, does_not_exist=[1])

    def test_invalid_account_param_raises(self) -> None:
        with pytest.raises(ValueError, match="Unknown account parameter"):
            ParamGridBuilder().account(does_not_exist=[1])

    def test_build_without_strategy_raises(self) -> None:
        with pytest.raises(ValueError, match="At least one strategy"):
            ParamGridBuilder().build()
