"""Tests for CS2 Game State Integration adapter."""

import pytest

from wled_controller.core.game_integration.adapters.cs2_adapter import CS2Adapter


# ── Realistic CS2 GSI payload samples ────────────────────────────────────


def _make_cs2_payload(
    *,
    health: int = 100,
    armor: int = 100,
    money: int = 800,
    kills: int = 0,
    deaths: int = 0,
    round_phase: str | None = None,
    bomb: str | None = None,
    flashed: int = 0,
    team: str = "CT",
    ammo_clip: int | None = None,
    ammo_clip_max: int | None = None,
    auth_token: str | None = None,
) -> dict:
    """Build a realistic CS2 GSI payload."""
    payload: dict = {
        "player": {
            "steamid": "76561198012345678",
            "name": "TestPlayer",
            "team": team,
            "state": {
                "health": health,
                "armor": armor,
                "helmet": True,
                "flashed": flashed,
                "smoked": 0,
                "burning": 0,
                "money": money,
                "round_kills": 0,
                "round_killhs": 0,
                "equip_value": 4400,
            },
            "match_stats": {
                "kills": kills,
                "assists": 0,
                "deaths": deaths,
                "mvps": 0,
                "score": kills * 2,
            },
            "weapons": {},
        },
    }

    if ammo_clip is not None and ammo_clip_max is not None:
        payload["player"]["weapons"]["weapon_0"] = {
            "name": "weapon_ak47",
            "paintkit": "default",
            "type": "Rifle",
            "ammo_clip": ammo_clip,
            "ammo_clip_max": ammo_clip_max,
            "ammo_reserve": 90,
            "state": "active",
        }

    if round_phase is not None:
        payload["round"] = {"phase": round_phase}

    if bomb is not None:
        payload.setdefault("round", {})["bomb"] = bomb

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

    return payload


# ── Health/Armor/Money tests ─────────────────────────────────────────────


class TestCS2ContinuousEvents:
    def test_health_normalized(self) -> None:
        payload = _make_cs2_payload(health=75)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "cs2_test"}, {})
        health_events = [e for e in events if e.event_type == "health"]
        assert len(health_events) == 1
        assert health_events[0].value == pytest.approx(0.75)

    def test_health_zero(self) -> None:
        payload = _make_cs2_payload(health=0)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "cs2_test"}, {})
        health_events = [e for e in events if e.event_type == "health"]
        assert health_events[0].value == 0.0

    def test_armor_normalized(self) -> None:
        payload = _make_cs2_payload(armor=50)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "cs2_test"}, {})
        armor_events = [e for e in events if e.event_type == "armor"]
        assert len(armor_events) == 1
        assert armor_events[0].value == pytest.approx(0.5)

    def test_money_normalized(self) -> None:
        payload = _make_cs2_payload(money=8000)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "cs2_test"}, {})
        gold_events = [e for e in events if e.event_type == "gold"]
        assert len(gold_events) == 1
        assert gold_events[0].value == pytest.approx(8000.0 / 16000.0)

    def test_ammo_normalized(self) -> None:
        payload = _make_cs2_payload(ammo_clip=15, ammo_clip_max=30)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "cs2_test"}, {})
        ammo_events = [e for e in events if e.event_type == "ammo"]
        assert len(ammo_events) == 1
        assert ammo_events[0].value == pytest.approx(0.5)

    def test_adapter_id_passed_through(self) -> None:
        payload = _make_cs2_payload(health=100)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "my_cs2"}, {})
        assert all(e.adapter_id == "my_cs2" for e in events)


# ── Kill/Death diff detection tests ──────────────────────────────────────


class TestCS2DiffDetection:
    def test_kill_detected_on_increase(self) -> None:
        payload1 = _make_cs2_payload(kills=3)
        _, state1 = CS2Adapter.parse_payload(payload1, {"adapter_id": "t"}, {})

        payload2 = _make_cs2_payload(kills=4)
        events, _ = CS2Adapter.parse_payload(payload2, {"adapter_id": "t"}, state1)
        kill_events = [e for e in events if e.event_type == "kill"]
        assert len(kill_events) == 1

    def test_no_kill_on_first_payload(self) -> None:
        payload = _make_cs2_payload(kills=5)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, {})
        kill_events = [e for e in events if e.event_type == "kill"]
        assert len(kill_events) == 0

    def test_multiple_kills_detected(self) -> None:
        payload1 = _make_cs2_payload(kills=2)
        _, state1 = CS2Adapter.parse_payload(payload1, {"adapter_id": "t"}, {})

        payload2 = _make_cs2_payload(kills=5)
        events, _ = CS2Adapter.parse_payload(payload2, {"adapter_id": "t"}, state1)
        kill_events = [e for e in events if e.event_type == "kill"]
        assert len(kill_events) == 3

    def test_death_detected(self) -> None:
        payload1 = _make_cs2_payload(deaths=0)
        _, state1 = CS2Adapter.parse_payload(payload1, {"adapter_id": "t"}, {})

        payload2 = _make_cs2_payload(deaths=1)
        events, _ = CS2Adapter.parse_payload(payload2, {"adapter_id": "t"}, state1)
        death_events = [e for e in events if e.event_type == "death"]
        assert len(death_events) == 1

    def test_no_death_on_same_count(self) -> None:
        payload1 = _make_cs2_payload(deaths=2)
        _, state1 = CS2Adapter.parse_payload(payload1, {"adapter_id": "t"}, {})

        payload2 = _make_cs2_payload(deaths=2)
        events, _ = CS2Adapter.parse_payload(payload2, {"adapter_id": "t"}, state1)
        death_events = [e for e in events if e.event_type == "death"]
        assert len(death_events) == 0


# ── Round/Bomb trigger tests ────────────────────────────────────────────


class TestCS2Triggers:
    def test_round_start(self) -> None:
        payload = _make_cs2_payload(round_phase="live")
        events, state = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, {})
        rs = [e for e in events if e.event_type == "round_start"]
        assert len(rs) == 1
        assert state["round_phase"] == "live"

    def test_round_end(self) -> None:
        prev = {"round_phase": "live"}
        payload = _make_cs2_payload(round_phase="over")
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, prev)
        re = [e for e in events if e.event_type == "round_end"]
        assert len(re) == 1

    def test_no_round_event_on_same_phase(self) -> None:
        prev = {"round_phase": "live"}
        payload = _make_cs2_payload(round_phase="live")
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, prev)
        round_events = [e for e in events if e.event_type in ("round_start", "round_end")]
        assert len(round_events) == 0

    def test_bomb_planted(self) -> None:
        payload = _make_cs2_payload(round_phase="live", bomb="planted")
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, {})
        bomb = [e for e in events if e.event_type == "objective_captured"]
        assert len(bomb) == 1

    def test_bomb_defused(self) -> None:
        prev = {"bomb": "planted"}
        payload = _make_cs2_payload(round_phase="live", bomb="defused")
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, prev)
        defuse = [e for e in events if e.event_type == "objective_lost"]
        assert len(defuse) == 1

    def test_flashbang_detected(self) -> None:
        payload = _make_cs2_payload(flashed=200)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, {"flashed": 0})
        flash = [e for e in events if e.event_type == "blinded"]
        assert len(flash) == 1
        assert flash[0].value == pytest.approx(200.0 / 255.0)

    def test_no_flashbang_when_already_flashed(self) -> None:
        prev = {"flashed": 200}
        payload = _make_cs2_payload(flashed=150)
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, prev)
        flash = [e for e in events if e.event_type == "blinded"]
        # Already flashed (prev > 0), so no new flash event
        assert len(flash) == 0

    def test_team_ct(self) -> None:
        payload = _make_cs2_payload(team="CT")
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, {})
        team = [e for e in events if e.event_type == "team_a"]
        assert len(team) == 1

    def test_team_t(self) -> None:
        payload = _make_cs2_payload(team="T")
        events, _ = CS2Adapter.parse_payload(payload, {"adapter_id": "t"}, {})
        team = [e for e in events if e.event_type == "team_b"]
        assert len(team) == 1


# ── Auth validation tests ───────────────────────────────────────────────


class TestCS2Auth:
    def test_auth_valid(self) -> None:
        payload = _make_cs2_payload(auth_token="mysecret")
        result = CS2Adapter.validate_auth({}, payload, {"auth_token": "mysecret"})
        assert result is True

    def test_auth_invalid(self) -> None:
        payload = _make_cs2_payload(auth_token="wrong")
        result = CS2Adapter.validate_auth({}, payload, {"auth_token": "mysecret"})
        assert result is False

    def test_auth_no_token_configured_accepts_all(self) -> None:
        payload = _make_cs2_payload()
        result = CS2Adapter.validate_auth({}, payload, {})
        assert result is True

    def test_auth_missing_payload_token(self) -> None:
        payload = _make_cs2_payload()  # no auth in payload
        result = CS2Adapter.validate_auth({}, payload, {"auth_token": "mysecret"})
        assert result is False


# ── Metadata tests ──────────────────────────────────────────────────────


class TestCS2Metadata:
    def test_adapter_type(self) -> None:
        assert CS2Adapter.ADAPTER_TYPE == "cs2"

    def test_supported_events(self) -> None:
        assert "health" in CS2Adapter.SUPPORTED_EVENTS
        assert "kill" in CS2Adapter.SUPPORTED_EVENTS
        assert "death" in CS2Adapter.SUPPORTED_EVENTS

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

    def test_setup_instructions(self) -> None:
        instructions = CS2Adapter.get_setup_instructions()
        assert "gamestate_integration" in instructions
        assert "CS2" in instructions
