"""Tests for League of Legends Live Client Data API adapter."""

import threading

import pytest

from wled_controller.core.game_integration.adapters.lol_adapter import (
    LoLAdapter,
    LoLPoller,
)


def _make_lol_payload(
    *,
    current_health: float = 1000.0,
    max_health: float = 1000.0,
    resource_value: float = 500.0,
    resource_max: float = 500.0,
    level: int = 10,
    summoner_name: str = "TestPlayer",
    current_gold: float | None = None,
) -> dict:
    """Build a realistic LoL Live Client Data payload."""
    payload: dict = {
        "activePlayer": {
            "summonerName": summoner_name,
            "level": level,
            "currentHealth": current_health,
            "championStats": {
                "currentHealth": current_health,
                "maxHealth": max_health,
                "resourceValue": resource_value,
                "resourceMax": resource_max,
                "attackDamage": 80.0,
                "abilityPower": 0.0,
                "armor": 60.0,
                "magicResist": 40.0,
                "moveSpeed": 345.0,
            },
        },
        "allPlayers": [
            {
                "summonerName": summoner_name,
                "championName": "Jinx",
                "team": "ORDER",
                "scores": {"kills": 5, "deaths": 2, "assists": 7},
            },
        ],
        "gameData": {
            "gameMode": "CLASSIC",
            "gameTime": 1200.5,
            "mapName": "Map11",
            "mapNumber": 11,
            "mapTerrain": "Default",
        },
    }

    if current_gold is not None:
        payload["allPlayers"][0]["currentGold"] = current_gold

    return payload


class TestLoLContinuousEvents:
    def test_health_normalized(self) -> None:
        payload = _make_lol_payload(current_health=500.0, max_health=1000.0)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, {})
        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_lol_payload(resource_value=300.0, resource_max=600.0)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, {})
        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_level_normalized(self) -> None:
        payload = _make_lol_payload(level=9)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, {})
        lvl = [e for e in events if e.event_type == "speed"]
        assert len(lvl) == 1
        assert lvl[0].value == pytest.approx(9.0 / 18.0)

    def test_level_max(self) -> None:
        payload = _make_lol_payload(level=18)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, {})
        lvl = [e for e in events if e.event_type == "speed"]
        assert lvl[0].value == pytest.approx(1.0)

    def test_gold(self) -> None:
        payload = _make_lol_payload(current_gold=15000.0)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, {})
        gold = [e for e in events if e.event_type == "gold"]
        assert len(gold) == 1
        assert gold[0].value == pytest.approx(15000.0 / 30000.0)


class TestLoLDeathRespawn:
    def test_death_detected_when_health_drops_to_zero(self) -> None:
        prev = {"alive": True}
        payload = _make_lol_payload(current_health=0.0, max_health=1000.0)
        events, state = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, prev)
        deaths = [e for e in events if e.event_type == "death"]
        assert len(deaths) == 1
        assert state["alive"] is False

    def test_no_death_when_already_dead(self) -> None:
        prev = {"alive": False}
        payload = _make_lol_payload(current_health=0.0, max_health=1000.0)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, prev)
        deaths = [e for e in events if e.event_type == "death"]
        assert len(deaths) == 0

    def test_respawn_detected(self) -> None:
        prev = {"alive": False}
        payload = _make_lol_payload(current_health=800.0, max_health=1000.0)
        events, state = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, prev)
        respawns = [e for e in events if e.event_type == "objective_progress"]
        assert len(respawns) == 1
        assert state["alive"] is True

    def test_no_respawn_when_already_alive(self) -> None:
        prev = {"alive": True}
        payload = _make_lol_payload(current_health=800.0, max_health=1000.0)
        events, _ = LoLAdapter.parse_payload(payload, {"adapter_id": "lol"}, prev)
        respawns = [e for e in events if e.event_type == "objective_progress"]
        assert len(respawns) == 0


class TestLoLAuth:
    def test_always_accepts(self) -> None:
        assert LoLAdapter.validate_auth({}, {}, {}) is True
        assert LoLAdapter.validate_auth({"X-Custom": "val"}, {}, {"auth_token": "x"}) is True


class TestLoLMetadata:
    def test_adapter_type(self) -> None:
        assert LoLAdapter.ADAPTER_TYPE == "lol"

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

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

    def test_setup_instructions(self) -> None:
        instructions = LoLAdapter.get_setup_instructions()
        assert "League of Legends" in instructions


class TestLoLPoller:
    def test_start_stop(self) -> None:
        """Test that the poller starts and stops cleanly."""
        called = threading.Event()

        def callback(data: dict) -> None:
            called.set()

        poller = LoLPoller({"poll_interval_ms": 100}, callback)
        assert not poller.is_running

        poller.start()
        assert poller.is_running

        poller.stop()
        assert not poller.is_running

    def test_double_start_no_crash(self) -> None:
        """Starting twice should not create duplicate threads."""
        poller = LoLPoller({"poll_interval_ms": 1000}, lambda d: None)
        poller.start()
        poller.start()  # should warn but not crash
        poller.stop()

    def test_stop_without_start(self) -> None:
        """Stopping without starting should not crash."""
        poller = LoLPoller({"poll_interval_ms": 1000}, lambda d: None)
        poller.stop()  # no-op
