"""Tests for StrategyConfig blueprint index properties."""

import polars as pl
import pytest
from tradepose_models.enums import Freq, OrderStrategy, TradeDirection, TrendType
from tradepose_models.strategy import Blueprint, StrategyConfig, Trigger

# ============ Test Fixtures ============


@pytest.fixture
def sample_trigger() -> Trigger:
    """Create a sample trigger for testing."""
    return Trigger(
        name="test_trigger",
        conditions=[pl.col("signal") == 1],
        offset_expr=pl.lit(0.0),
        order_strategy=OrderStrategy.IMMEDIATE_ENTRY,
        priority=1,
    )


@pytest.fixture
def base_blueprint(sample_trigger: Trigger) -> Blueprint:
    """Create a base blueprint for testing."""
    return Blueprint(
        name="base",
        direction=TradeDirection.LONG,
        trend_type=TrendType.TREND,
        entry_triggers=[sample_trigger],
        exit_triggers=[],
    )


@pytest.fixture
def advanced_blueprints() -> list[Blueprint]:
    """Create sample advanced blueprints for testing."""
    trigger = Trigger(
        name="entry",
        conditions=[],
        offset_expr=pl.lit(0.0),
        order_strategy=OrderStrategy.FAVORABLE_DELAY_ENTRY,
        priority=1,
    )
    return [
        Blueprint(
            name="L_fav_0.1",
            direction=TradeDirection.LONG,
            trend_type=TrendType.TREND,
            entry_triggers=[trigger],
            exit_triggers=[],
        ),
        Blueprint(
            name="L_fav_0.2",
            direction=TradeDirection.LONG,
            trend_type=TrendType.TREND,
            entry_triggers=[trigger],
            exit_triggers=[],
        ),
        Blueprint(
            name="L_fav_0.3",
            direction=TradeDirection.LONG,
            trend_type=TrendType.TREND,
            entry_triggers=[trigger],
            exit_triggers=[],
        ),
    ]


@pytest.fixture
def base_config(base_blueprint: Blueprint) -> StrategyConfig:
    """Create a base StrategyConfig for testing."""
    return StrategyConfig(
        name="test_strategy",
        base_instrument="US100",
        base_freq=Freq.HOUR_1,
        base_blueprint=base_blueprint,
        advanced_blueprints=[],
    )


@pytest.fixture
def config_with_blueprints(
    base_blueprint: Blueprint, advanced_blueprints: list[Blueprint]
) -> StrategyConfig:
    """Create a StrategyConfig with advanced blueprints."""
    return StrategyConfig(
        name="test_strategy",
        base_instrument="US100",
        base_freq=Freq.HOUR_1,
        base_blueprint=base_blueprint,
        advanced_blueprints=advanced_blueprints,
    )


# ============ Test StrategyConfig Properties ============


class TestAdvBpProperties:
    """Tests for adv_bp_map and adv_bp_summary properties."""

    def test_adv_bp_map_empty(self, base_config: StrategyConfig):
        """adv_bp_map should return empty dict for no advanced_blueprints."""
        assert base_config.adv_bp_map == {}

    def test_adv_bp_map_with_blueprints(self, config_with_blueprints: StrategyConfig):
        """adv_bp_map should return name->blueprint mapping."""
        bp_map = config_with_blueprints.adv_bp_map
        assert len(bp_map) == 3
        assert "L_fav_0.1" in bp_map
        assert "L_fav_0.2" in bp_map
        assert "L_fav_0.3" in bp_map
        assert isinstance(bp_map["L_fav_0.1"], Blueprint)

    def test_adv_bp_summary_empty(self, base_config: StrategyConfig):
        """adv_bp_summary should return empty list for no advanced_blueprints."""
        assert base_config.adv_bp_summary == []

    def test_adv_bp_summary_with_blueprints(self, config_with_blueprints: StrategyConfig):
        """adv_bp_summary should return list of dicts."""
        summary = config_with_blueprints.adv_bp_summary
        assert len(summary) == 3
        assert summary[0] == {"idx": 0, "name": "L_fav_0.1", "direction": "Long"}
        assert summary[1] == {"idx": 1, "name": "L_fav_0.2", "direction": "Long"}
        assert summary[2] == {"idx": 2, "name": "L_fav_0.3", "direction": "Long"}


# ============ Test get_adv_bp ============


class TestGetAdvBp:
    """Tests for get_adv_bp method."""

    def test_get_by_index(self, config_with_blueprints: StrategyConfig):
        """Should get blueprint by index."""
        bp = config_with_blueprints.get_adv_bp(1)
        assert bp.name == "L_fav_0.2"

    def test_get_by_name(self, config_with_blueprints: StrategyConfig):
        """Should get blueprint by name."""
        bp = config_with_blueprints.get_adv_bp("L_fav_0.3")
        assert bp.name == "L_fav_0.3"

    def test_get_by_invalid_index(self, config_with_blueprints: StrategyConfig):
        """Should raise IndexError for invalid index."""
        with pytest.raises(IndexError):
            config_with_blueprints.get_adv_bp(10)

    def test_get_by_invalid_name(self, config_with_blueprints: StrategyConfig):
        """Should raise KeyError for invalid name."""
        with pytest.raises(KeyError):
            config_with_blueprints.get_adv_bp("nonexistent")


# ============ Test with_adv_blueprints ============


class TestWithAdvBlueprints:
    """Tests for with_adv_blueprints method."""

    def test_replace_empty_with_blueprints(
        self, base_config: StrategyConfig, advanced_blueprints: list[Blueprint]
    ):
        """Should replace empty advanced_blueprints with new list."""
        new_config = base_config.with_adv_blueprints(advanced_blueprints)
        assert len(new_config.advanced_blueprints) == 3
        assert new_config.advanced_blueprints[0].name == "L_fav_0.1"
        # Original should be unchanged
        assert len(base_config.advanced_blueprints) == 0

    def test_replace_existing_blueprints(
        self, config_with_blueprints: StrategyConfig, sample_trigger: Trigger
    ):
        """Should replace existing advanced_blueprints."""
        new_bp = Blueprint(
            name="new_bp",
            direction=TradeDirection.SHORT,
            trend_type=TrendType.REVERSAL,
            entry_triggers=[sample_trigger],
            exit_triggers=[],
        )
        new_config = config_with_blueprints.with_adv_blueprints([new_bp])
        assert len(new_config.advanced_blueprints) == 1
        assert new_config.advanced_blueprints[0].name == "new_bp"
        # Original should be unchanged
        assert len(config_with_blueprints.advanced_blueprints) == 3

    def test_preserves_other_fields(
        self, base_config: StrategyConfig, advanced_blueprints: list[Blueprint]
    ):
        """Should preserve all other config fields."""
        new_config = base_config.with_adv_blueprints(advanced_blueprints)
        assert new_config.name == base_config.name
        assert new_config.base_instrument == base_config.base_instrument
        assert new_config.base_freq == base_config.base_freq
        assert new_config.base_blueprint == base_config.base_blueprint

    def test_with_empty_list(self, config_with_blueprints: StrategyConfig):
        """Should allow setting empty list."""
        new_config = config_with_blueprints.with_adv_blueprints([])
        assert len(new_config.advanced_blueprints) == 0
