from __future__ import annotations

import asyncio
from pathlib import Path
from types import SimpleNamespace

from hockey_data.agent_bridge import VmixAgentHub
from hockey_data.auth_bridge import HockeyUser
from hockey_data.models import OperatorSession
from tests.support import LocalTestDatabase


class FakeWebSocket:
    def __init__(self) -> None:
        self.client = SimpleNamespace(host="127.0.0.1")
        self.sent: list[dict] = []
        self.closed = False

    async def send_json(self, payload: dict) -> None:
        self.sent.append(payload)

    async def close(self, **_kwargs) -> None:
        self.closed = True


async def _ack_latest(hub: VmixAgentHub, device_id: str, ws: FakeWebSocket) -> dict:
    command = None
    for _ in range(100):
        await asyncio.sleep(0)
        commands = [item for item in ws.sent if item.get("type") == "vmix.command"]
        if commands:
            command = commands[-1]
            break
    assert command is not None
    await hub.receive_command_ack(
        device_id,
        {"type": "command.ack", "request_id": command["request_id"], "ok": True},
    )
    return command


def test_multiple_agents_stay_enabled_and_runtime_routes_to_explicit_browser_agent(tmp_path: Path) -> None:
    database = LocalTestDatabase(tmp_path / "multi-agent.sqlite3")
    database.create_all()
    hub = VmixAgentHub(database)  # type: ignore[arg-type]
    user = HockeyUser(id="multi-1", login="multi", display_name="Multi")
    ws_test = FakeWebSocket()
    ws_live = FakeWebSocket()

    async def scenario() -> None:
        for device_id, secret, ws in (
            ("GFX-MULTI-TEST", "t" * 40, ws_test),
            ("GFX-MULTI-LIVE", "l" * 40, ws_live),
        ):
            await hub.register(
                ws,  # type: ignore[arg-type]
                {
                    "device_id": device_id,
                    "device_secret": secret,
                    "device_name": device_id,
                    "hostname": device_id,
                    "agent_version": "1.4.0",
                    "vmix": {"connected": True, "url": "http://127.0.0.1:8088/api/"},
                },
            )
            await hub.pair_device(device_id, user)

        devices = await hub.list_for_user(user)
        assert set(devices["active_device_ids"]) == {"GFX-MULTI-TEST", "GFX-MULTI-LIVE"}

        with database.session() as session:
            session.add(
                OperatorSession(
                    session_token="multi-session",
                    wfl_user_id=user.id,
                    login_snapshot=user.login,
                    tournament_external_id="1437",
                    game_external_id="902918",
                    status="active",
                )
            )

        assert await hub.assign_match(
            wfl_user_id=user.id,
            tournament_external_id="1437",
            game_external_id="902918",
            device_id="GFX-MULTI-TEST",
            operator_session_token="multi-session",
        )
        assert await hub.assign_match(
            wfl_user_id=user.id,
            tournament_external_id="1437",
            game_external_id="902918",
            device_id="GFX-MULTI-LIVE",
            operator_session_token="multi-session",
        )

        # Ignore pairing / assignment traffic and count only runtime vmix.command messages.
        before_test = len([item for item in ws_test.sent if item.get("type") == "vmix.command"])
        before_live = len([item for item in ws_live.sent if item.get("type") == "vmix.command"])

        task_test = asyncio.create_task(
            hub.run_vmix_sequence_for_user(
                user,
                [{"Function": "OverlayInput1In", "Input": "score-key"}],
                device_id="GFX-MULTI-TEST",
                session_token="multi-session",
            )
        )
        command_test = await _ack_latest(hub, "GFX-MULTI-TEST", ws_test)
        result_test = await task_test
        assert result_test["device_id"] == "GFX-MULTI-TEST"
        assert command_test["command"]["Function"] == "OverlayInput1In"
        assert len([item for item in ws_test.sent if item.get("type") == "vmix.command"]) == before_test + 1
        assert len([item for item in ws_live.sent if item.get("type") == "vmix.command"]) == before_live

        task_live = asyncio.create_task(
            hub.run_vmix_sequence_for_user(
                user,
                [{"Function": "OverlayInput1Out", "Input": "score-key"}],
                device_id="GFX-MULTI-LIVE",
                session_token="multi-session",
            )
        )
        command_live = await _ack_latest(hub, "GFX-MULTI-LIVE", ws_live)
        result_live = await task_live
        assert result_live["device_id"] == "GFX-MULTI-LIVE"
        assert command_live["command"]["Function"] == "OverlayInput1Out"
        assert len([item for item in ws_live.sent if item.get("type") == "vmix.command"]) == before_live + 1
        assert len([item for item in ws_test.sent if item.get("type") == "vmix.command"]) == before_test + 1

    asyncio.run(scenario())
