from __future__ import annotations

from typing import Any, Dict, List

import pandas as pd

from deck_builder.theme_context import ThemeContext, ThemeTarget
from deck_builder.phases.phase4_spells import SpellAdditionMixin
from deck_builder import builder_utils as bu


class DummyRNG:
    def uniform(self, _a: float, _b: float) -> float:
        return 1.0

    def random(self) -> float:
        return 0.0

    def choice(self, seq):
        return seq[0]


class DummySpellBuilder(SpellAdditionMixin):
    def __init__(self, df: pd.DataFrame, context: ThemeContext):
        self._combined_cards_df = df
        # Pre-populate 99 cards so we target a single filler slot
        self.card_library: Dict[str, Dict[str, Any]] = {
            f"Existing{i}": {"Count": 1} for i in range(99)
        }
        self.primary_tag = context.ordered_targets[0].display if context.ordered_targets else None
        self.secondary_tag = None
        self.tertiary_tag = None
        self.tag_mode = context.combine_mode
        self.prefer_owned = False
        self.owned_card_names: set[str] = set()
        self.bracket_limits: Dict[str, Any] = {}
        self.output_log: List[str] = []
        self.output_func = self.output_log.append
        self._rng = DummyRNG()
        self._theme_context = context
        self.added_cards: List[str] = []

    def _get_rng(self) -> DummyRNG:
        return self._rng

    @property
    def rng(self) -> DummyRNG:
        return self._rng

    def get_theme_context(self) -> ThemeContext:  # type: ignore[override]
        return self._theme_context

    def add_card(self, name: str, **kwargs: Any) -> None:  # type: ignore[override]
        self.card_library[name] = {"Count": kwargs.get("count", 1)}
        self.added_cards.append(name)


def make_context(user_theme_weight: float) -> ThemeContext:
    user = ThemeTarget(
        role="user_1",
        display="Angels",
        slug="angels",
        source="user",
        weight=1.0,
    )
    return ThemeContext(
        ordered_targets=[user],
        combine_mode="AND",
        weights={"user_1": 1.0},
        commander_slugs=[],
        user_slugs=["angels"],
        resolution=None,
        user_theme_weight=user_theme_weight,
    )


def build_dataframe() -> pd.DataFrame:
    return pd.DataFrame(
        [
            {
                "name": "Angel Song",
                "type": "Instant",
                "themeTags": ["Angels"],
                "manaValue": 2,
                "edhrecRank": 1400,
            },
        ]
    )


def test_user_theme_bonus_increases_weight(monkeypatch) -> None:
    captured: List[List[tuple[str, float]]] = []

    def fake_weighted(pool: List[tuple[str, float]], k: int, rng=None) -> List[str]:
        captured.append(list(pool))
        ranked = sorted(pool, key=lambda item: item[1], reverse=True)
        return [name for name, _ in ranked[:k]]

    monkeypatch.setattr(bu, "weighted_sample_without_replacement", fake_weighted)

    def run(user_weight: float) -> Dict[str, float]:
        start = len(captured)
        context = make_context(user_weight)
        builder = DummySpellBuilder(build_dataframe(), context)
        builder.fill_remaining_theme_spells()
        assert start < len(captured)  # ensure we captured weights
        pool = captured[start]
        return dict(pool)

    weights_no_bonus = run(1.0)
    weights_bonus = run(1.5)

    assert "Angel Song" in weights_no_bonus
    assert "Angel Song" in weights_bonus
    assert weights_bonus["Angel Song"] > weights_no_bonus["Angel Song"]
