"""Tests for Dota 2 Game State Integration adapter."""

import pytest

from wled_controller.core.game_integration.adapters.dota2_adapter import Dota2Adapter


def _make_dota2_payload(
    *,
    health: int = 1000,
    max_health: int = 1000,
    mana: float = 500.0,
    max_mana: float = 500.0,
    gold: int = 625,
    kills: int = 0,
    deaths: int = 0,
    game_state: str | None = None,
    auth_token: str | None = None,
) -> dict:
    """Build a realistic Dota 2 GSI payload."""
    payload: dict = {
        "hero": {
            "xpos": -6624,
            "ypos": -6592,
            "id": 1,
            "name": "npc_dota_hero_antimage",
            "level": 12,
            "alive": True,
            "respawn_seconds": 0,
            "buyback_cost": 874,
            "buyback_cooldown": 0,
            "health": health,
            "max_health": max_health,
            "health_percent": int(100.0 * health / max_health) if max_health else 0,
            "mana": mana,
            "max_mana": max_mana,
            "mana_percent": int(100.0 * mana / max_mana) if max_mana else 0,
        },
        "player": {
            "steamid": "76561198012345678",
            "name": "TestPlayer",
            "activity": "playing",
            "kills": kills,
            "deaths": deaths,
            "assists": 0,
            "last_hits": 120,
            "denies": 10,
            "kill_streak": 0,
            "gold": gold,
            "gold_reliable": 200,
            "gold_unreliable": gold - 200,
            "gpm": 450,
            "xpm": 520,
        },
    }

    if game_state is not None:
        payload["map"] = {
            "name": "start",
            "matchid": "12345",
            "game_time": 1200,
            "clock_time": 1200,
            "daytime": True,
            "game_state": game_state,
            "win_team": "none",
        }

    if auth_token is not None:
        payload["auth"] = {"token": auth_token}

    return payload


class TestDota2ContinuousEvents:
    def test_health_normalized(self) -> None:
        payload = _make_dota2_payload(health=750, max_health=1500)
        events, _ = Dota2Adapter.parse_payload(payload, {"adapter_id": "d2"}, {})
        hp = [e for e in events if e.event_type == "health"]
        assert len(hp) == 1
        assert hp[0].value == pytest.approx(0.5)

    def test_mana_normalized(self) -> None:
        payload = _make_dota2_payload(mana=300.0, max_mana=600.0)
        events, _ = Dota2Adapter.parse_payload(payload, {"adapter_id": "d2"}, {})
        mp = [e for e in events if e.event_type == "mana"]
        assert len(mp) == 1
        assert mp[0].value == pytest.approx(0.5)

    def test_gold_normalized(self) -> None:
        payload = _make_dota2_payload(gold=5000)
        events, _ = Dota2Adapter.parse_payload(payload, {"adapter_id": "d2"}, {})
        g = [e for e in events if e.event_type == "gold"]
        assert len(g) == 1
        assert g[0].value == pytest.approx(5000.0 / 99999.0)

    def test_gold_custom_max(self) -> None:
        payload = _make_dota2_payload(gold=5000)
        events, _ = Dota2Adapter.parse_payload(
            payload,
            {"adapter_id": "d2", "max_gold": 10000},
            {},
        )
        g = [e for e in events if e.event_type == "gold"]
        assert g[0].value == pytest.approx(0.5)


class TestDota2DiffDetection:
    def test_kill_detected(self) -> None:
        p1 = _make_dota2_payload(kills=3)
        _, s1 = Dota2Adapter.parse_payload(p1, {"adapter_id": "d2"}, {})

        p2 = _make_dota2_payload(kills=4)
        events, _ = Dota2Adapter.parse_payload(p2, {"adapter_id": "d2"}, s1)
        kills = [e for e in events if e.event_type == "kill"]
        assert len(kills) == 1

    def test_no_kill_on_first_payload(self) -> None:
        p = _make_dota2_payload(kills=5)
        events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, {})
        kills = [e for e in events if e.event_type == "kill"]
        assert len(kills) == 0

    def test_death_detected(self) -> None:
        p1 = _make_dota2_payload(deaths=1)
        _, s1 = Dota2Adapter.parse_payload(p1, {"adapter_id": "d2"}, {})

        p2 = _make_dota2_payload(deaths=2)
        events, _ = Dota2Adapter.parse_payload(p2, {"adapter_id": "d2"}, s1)
        deaths = [e for e in events if e.event_type == "death"]
        assert len(deaths) == 1

    def test_multiple_kills(self) -> None:
        p1 = _make_dota2_payload(kills=0)
        _, s1 = Dota2Adapter.parse_payload(p1, {"adapter_id": "d2"}, {})

        p2 = _make_dota2_payload(kills=3)
        events, _ = Dota2Adapter.parse_payload(p2, {"adapter_id": "d2"}, s1)
        kills = [e for e in events if e.event_type == "kill"]
        assert len(kills) == 3


class TestDota2MatchFlow:
    def test_match_start_pre_game(self) -> None:
        p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_PRE_GAME")
        events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, {})
        ms = [e for e in events if e.event_type == "match_start"]
        assert len(ms) == 1

    def test_match_start_in_progress(self) -> None:
        prev = {"game_state": "DOTA_GAMERULES_STATE_PRE_GAME"}
        p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_GAME_IN_PROGRESS")
        events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, prev)
        ms = [e for e in events if e.event_type == "match_start"]
        assert len(ms) == 1

    def test_match_end(self) -> None:
        prev = {"game_state": "DOTA_GAMERULES_STATE_GAME_IN_PROGRESS"}
        p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_POST_GAME")
        events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, prev)
        me = [e for e in events if e.event_type == "match_end"]
        assert len(me) == 1

    def test_no_event_on_same_state(self) -> None:
        prev = {"game_state": "DOTA_GAMERULES_STATE_GAME_IN_PROGRESS"}
        p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_GAME_IN_PROGRESS")
        events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, prev)
        flow = [e for e in events if e.event_type in ("match_start", "match_end")]
        assert len(flow) == 0


class TestDota2Auth:
    def test_auth_valid(self) -> None:
        p = _make_dota2_payload(auth_token="secret")
        result = Dota2Adapter.validate_auth({}, p, {"auth_token": "secret"})
        assert result is True

    def test_auth_invalid(self) -> None:
        p = _make_dota2_payload(auth_token="wrong")
        result = Dota2Adapter.validate_auth({}, p, {"auth_token": "secret"})
        assert result is False

    def test_auth_no_config_accepts_all(self) -> None:
        p = _make_dota2_payload()
        result = Dota2Adapter.validate_auth({}, p, {})
        assert result is True


class TestDota2Metadata:
    def test_adapter_type(self) -> None:
        assert Dota2Adapter.ADAPTER_TYPE == "dota2"

    def test_supported_events(self) -> None:
        assert "health" in Dota2Adapter.SUPPORTED_EVENTS
        assert "mana" in Dota2Adapter.SUPPORTED_EVENTS
        assert "gold" in Dota2Adapter.SUPPORTED_EVENTS

    def test_config_schema(self) -> None:
        schema = Dota2Adapter.get_config_schema()
        assert "auth_token" in schema["properties"]
        assert "max_gold" in schema["properties"]

    def test_setup_instructions(self) -> None:
        instructions = Dota2Adapter.get_setup_instructions()
        assert "Dota 2" in instructions
