from pathlib import Path

from hockey_data.agent_bridge import VmixAgentHub
from hockey_data.models import VmixPreparedTitle

ROOT = Path(__file__).resolve().parents[1]
APP = (ROOT / "ui_builder/static/app.js").read_text(encoding="utf-8")
MAPPING = (ROOT / "hockey_data/mapping_context.py").read_text(encoding="utf-8")
BRIDGE = (ROOT / "hockey_data/agent_bridge.py").read_text(encoding="utf-8")
STYLES = (ROOT / "ui_builder/static/styles.css").read_text(encoding="utf-8")


def test_home_and_away_penalties_have_independent_manual_selection():
    assert 'selectedPreviewEventIds: { home: "", away: "" }' in APP
    assert 'board.selectedPreviewEventIds[side] = event.id' in APP
    assert 'card.classList.toggle("is-preview-selected", hockeyPenaltyIsPreviewSelected(board, event));' in APP
    # The old global selectedEventId must not drive the visual selection class.
    penalty_start = APP.index("function hockeyPenaltyCard")
    penalty_end = APP.index("function createHockeyDraftFromDrop", penalty_start)
    penalty_card = APP[penalty_start:penalty_end]
    assert 'card.classList.toggle("is-selected", board.selectedEventId === event.id)' not in penalty_card
    assert '.hpd-team-penalty-card.team-home.is-preview-selected' in STYLES
    assert '.hpd-team-penalty-card.team-away.is-preview-selected' in STYLES


def test_selected_penalty_context_is_manual_and_active_penalty_context_is_separate():
    select_start = APP.index("async function hockeySelectPenaltyForPreview")
    select_end = APP.index("function hockeyPenaltyCard", select_start)
    select_block = APP[select_start:select_end]
    for key in (
        "selected_home_penalty_id",
        "selected_home_penalty_player_id",
        "selected_home_penalty_player_db_id",
        "selected_home_penalty_team_penalty",
        "selected_away_penalty_id",
        "selected_away_penalty_player_id",
        "selected_away_penalty_player_db_id",
        "selected_away_penalty_team_penalty",
    ):
        assert key in select_block
        assert key in MAPPING
    sync_start = APP.index("async function hockeySyncPenaltySideMappingContext")
    sync_end = APP.index("function penaltyTargetAssignmentKey", sync_start)
    sync_block = APP[sync_start:sync_end]
    assert "active_home_penalty_id:" in sync_block
    assert "active_away_penalty_id:" in sync_block
    assert "selected_home_penalty_id:" not in sync_block
    assert "selected_away_penalty_id:" not in sync_block


def test_player_selection_values_survive_reload_using_server_state_and_browser_backup():
    for token in (
        "function hockeyPlayerSelectionStorageKey",
        "function hockeyBackupPlayerSelectionValues",
        "function hockeyStoredPlayerSelectionValues",
        "hockeyBackupPlayerSelectionValues(gameId, payload?.values || {})",
        'if (!control || String(control.game_id || "") !== String(gameId)) return false;',
        "const hasServerSelectionKeys",
    ):
        assert token in APP
    assert "localStorage.setItem(hockeyPlayerSelectionStorageKey(id)" in APP


def test_player_block_editor_is_compact_single_line():
    assert "/* BUILD84 — compact, single-line player-block editor. */" in STYLES
    assert "min-height:48px" in STYLES
    assert "grid-template-columns:24px 66px" in STYLES
    assert '> Главный</label>' in APP
    assert '> Свёрнут</label>' in APP


def test_prepared_titles_workspace_exists_and_clones_to_vmix():
    assert '{ id: "prepared_titles", label: "Заготовки" }' in APP
    for token in (
        "function renderHockeyPreparedTitlesWorkspace()",
        "function hockeyOpenPreparedFromPlayerPanel(panel)",
        "data-player-panel-prepared",
        "/api/hockey/prepared-titles",
        "Сохранить новым Input",
        "Подставить из Mapping",
        "▶ Preview",
    ):
        assert token in APP
    assert VmixPreparedTitle.__tablename__ == "hockey_vmix_prepared_titles"
    assert '{"Function": "CreateVirtualInput", "Input": source_ref}' in BRIDGE
    assert '{"Function": "PreviewInput", "Input": input_ref}' in BRIDGE
    assert '@router.post("/api/hockey/prepared-titles")' in BRIDGE
    assert '@router.get("/api/hockey/prepared-titles/mapping-snapshot")' in BRIDGE


def test_prepared_input_resolution_prefers_stable_key_then_number_then_unique_title():
    inventory = {
        "inputs": [
            {"key": "stable-a", "number": "7", "title": "TITLE"},
            {"key": "stable-b", "number": "9", "title": "OTHER"},
        ]
    }
    assert VmixAgentHub._prepared_find_input(inventory, key="stable-b")["number"] == "9"
    assert VmixAgentHub._prepared_find_input(inventory, number="7")["key"] == "stable-a"
    assert VmixAgentHub._prepared_find_input(inventory, title="OTHER")["key"] == "stable-b"
    duplicate = {"inputs": [{"key": "a", "title": "SAME"}, {"key": "b", "title": "SAME"}]}
    assert VmixAgentHub._prepared_find_input(duplicate, title="SAME") is None


def test_prepared_title_creation_clones_last_input_and_applies_field_snapshot(tmp_path):
    import asyncio
    import json
    import types
    from types import SimpleNamespace
    from sqlalchemy import select
    from hockey_data.auth_bridge import HockeyUser
    from hockey_data.models import VmixDevice, VmixPreparedTitle
    from tests.support import LocalTestDatabase

    database = LocalTestDatabase(tmp_path / "prepared.sqlite3")
    database.create_all()
    hub = VmixAgentHub(database)  # type: ignore[arg-type]
    user = HockeyUser(id="prep-user", login="prep", display_name="Prep")
    source_inventory = {
        "inputs": [
            {
                "key": "source-key",
                "number": "12",
                "title": "COMPARE",
                "type": "GT",
                "fields": [
                    {"name": "Name.Text", "type": "text", "index": "0"},
                    {"name": "Photo.Source", "type": "image", "index": "1"},
                ],
            }
        ]
    }
    with database.session() as session:
        session.add(VmixDevice(
            device_uuid="prep-device",
            device_secret_hash="x" * 64,
            name="Prep device",
            wfl_user_id=user.id,
            is_active_for_account=True,
            vmix_connected=True,
            current_match_external_id="9001",
            project_inventory_json=json.dumps(source_inventory),
        ))

    calls = []

    async def fake_sequence(self, _user, commands, *, device_id="", session_token="", timeout=0):
        calls.append(commands)
        if commands and commands[0].get("Function") == "CreateVirtualInput":
            cloned = {
                "inputs": source_inventory["inputs"] + [
                    {
                        "key": "clone-key",
                        "number": "13",
                        "title": "COMPARE",
                        "type": "GT",
                        "fields": source_inventory["inputs"][0]["fields"],
                    }
                ]
            }
            with database.session() as session:
                device = session.scalar(select(VmixDevice).where(VmixDevice.device_uuid == "prep-device"))
                device.project_inventory_json = json.dumps(cloned)
        return {"ok": True, "device_id": device_id or "prep-device", "match_id": "9001"}

    hub.run_vmix_sequence_for_user = types.MethodType(fake_sequence, hub)
    payload = SimpleNamespace(
        name="Compare ready",
        device_id="prep-device",
        session_token="session",
        source_input_key="source-key",
        source_input_number="12",
        source_input_title="COMPARE",
        source_kind="player_selection",
        source_ref="compare_home",
        field_values={
            "Name.Text": {"type": "text", "value": "Иванов"},
            "Photo.Source": {"type": "image", "value": r"D:\\Photo\\ivanov.png"},
        },
    )

    result = asyncio.run(hub.create_prepared_title(user, payload))
    assert result["clone_input"]["number"] == "13"
    assert calls[0] == [{"Function": "CreateVirtualInput", "Input": "source-key"}]
    assert any(command.get("Function") == "SetText" and command.get("Input") == "clone-key" for command in calls[1])
    assert any(command.get("Function") == "SetImage" and command.get("SelectedName") == "Photo.Source" for command in calls[1])
    with database.session() as session:
        row = session.scalar(select(VmixPreparedTitle))
        assert row is not None
        assert row.clone_input_key == "clone-key"
        assert row.clone_input_number == "13"
        assert row.source_ref == "compare_home"
