"""Tests for multi-strategy orchestration (dict pipeline support)."""

import os
import pytest

os.environ.setdefault("HORIZON_API_KEY", "test-key-123")

from horizon._horizon import Market, Quote
from horizon.context import Context, FeedData, InventorySnapshot
from horizon.strategy import _run_pipeline


def _make_ctx(market_id="mkt1"):
    market = Market(id=market_id, name=market_id, slug=market_id)
    return Context(
        feeds={"feed": FeedData(price=0.5, bid=0.45, ask=0.55, timestamp=1.0)},
        inventory=InventorySnapshot(),
        market=market,
        params={},
    )


def _strategy_a(ctx: Context) -> list[Quote]:
    """Strategy A: tight quotes."""
    return [Quote(bid=0.48, ask=0.52, size=5.0)]


def _strategy_b(ctx: Context) -> list[Quote]:
    """Strategy B: wide quotes."""
    return [Quote(bid=0.40, ask=0.60, size=10.0)]


_strategy_a.__name__ = "strategy_a"
_strategy_b.__name__ = "strategy_b"


class TestRunPipelineWithList:
    """Existing list pipeline behavior (backward compat)."""

    def test_basic_list_pipeline(self):
        result = _run_pipeline([_strategy_a], _make_ctx())
        assert isinstance(result, list)
        assert len(result) == 1
        assert abs(result[0].bid - 0.48) < 1e-6

    def test_chained_pipeline(self):
        def modifier(ctx, quotes):
            return quotes  # passthrough

        modifier.__name__ = "modifier"
        result = _run_pipeline([_strategy_a, modifier], _make_ctx())
        assert isinstance(result, list)


class TestMultiStrategyDict:
    """Dict pipeline: market_id -> pipeline mapping."""

    def test_market_specific_pipeline(self):
        pipeline = {
            "mkt_a": [_strategy_a],
            "mkt_b": [_strategy_b],
        }
        # Market A should get strategy A
        ctx_a = _make_ctx("mkt_a")
        market_pipeline = pipeline.get("mkt_a")
        result_a = _run_pipeline(market_pipeline, ctx_a)
        assert abs(result_a[0].bid - 0.48) < 1e-6

        # Market B should get strategy B
        ctx_b = _make_ctx("mkt_b")
        market_pipeline = pipeline.get("mkt_b")
        result_b = _run_pipeline(market_pipeline, ctx_b)
        assert abs(result_b[0].bid - 0.40) < 1e-6

    def test_wildcard_default(self):
        pipeline = {
            "mkt_a": [_strategy_a],
            "*": [_strategy_b],
        }
        # Unknown market falls back to "*"
        market_pipeline = pipeline.get("mkt_c", pipeline.get("*"))
        assert market_pipeline is not None
        result = _run_pipeline(market_pipeline, _make_ctx("mkt_c"))
        assert abs(result[0].bid - 0.40) < 1e-6

    def test_no_matching_pipeline_returns_none(self):
        pipeline = {
            "mkt_a": [_strategy_a],
        }
        market_pipeline = pipeline.get("mkt_z", pipeline.get("*"))
        assert market_pipeline is None

    def test_dict_pipeline_resolution(self):
        """Simulate the _run_loop pipeline resolution logic."""
        pipeline = {
            "mkt_a": [_strategy_a],
            "*": [_strategy_b],
        }

        test_cases = [
            ("mkt_a", 0.48),  # Specific match
            ("mkt_b", 0.40),  # Wildcard fallback
            ("mkt_c", 0.40),  # Wildcard fallback
        ]

        for market_id, expected_bid in test_cases:
            if isinstance(pipeline, dict):
                market_pipeline = pipeline.get(market_id, pipeline.get("*"))
                if market_pipeline is None:
                    continue
            else:
                market_pipeline = pipeline

            result = _run_pipeline(market_pipeline, _make_ctx(market_id))
            assert abs(result[0].bid - expected_bid) < 1e-6, (
                f"market={market_id}: expected bid={expected_bid}, got {result[0].bid}"
            )


class TestMultiStrategyValidation:
    """Validation of dict pipeline in run()."""

    def test_empty_dict_raises(self):
        from horizon.strategy import run
        with pytest.raises(ValueError, match="pipeline dict must not be empty"):
            run(
                name="test",
                pipeline={},
                markets=["m1"],
                mode="paper",
                db_path=None,
            )

    def test_empty_list_value_raises(self):
        from horizon.strategy import run
        with pytest.raises(ValueError, match="pipeline.*must be a non-empty list"):
            run(
                name="test",
                pipeline={"mkt1": []},
                markets=["m1"],
                mode="paper",
                db_path=None,
            )

    def test_non_list_value_raises(self):
        from horizon.strategy import run
        with pytest.raises(ValueError, match="pipeline.*must be a non-empty list"):
            run(
                name="test",
                pipeline={"mkt1": _strategy_a},  # Not a list
                markets=["m1"],
                mode="paper",
                db_path=None,
            )
