from __future__ import annotations

import asyncio
import json
from pathlib import Path

from sqlalchemy import select

from hockey_data.agent_bridge import VmixAgentHub
from hockey_data.auth_bridge import HockeyUser
from hockey_data.models import VmixDevice, VmixMappingField, VmixMappingProfile
from tests.support import LocalTestDatabase

ROOT = Path(__file__).resolve().parents[1]
JS = (ROOT / "hockey_data/static/admin-directories.js").read_text(encoding="utf-8")
CSS = (ROOT / "hockey_data/static/admin-directories.css").read_text(encoding="utf-8")


def test_mapping_heavy_data_sections_are_collapsed_by_default() -> None:
    assert 'data-map-sql-cell-details' in JS
    assert 'data-map-sql-table-details' in JS
    assert 'mappingSqlCellOpen: false' in JS
    assert 'mappingSqlTableOpen: false' in JS
    assert 'mappingOpenDataGroups: {}' in JS
    assert 'data-map-data-group="${escapeHtml(category.name)}"' in JS
    assert 'index < 3 || query' not in JS
    assert 'query || state.mappingOpenDataGroups[category.name]' in JS
    assert '.hockey-map-sql-cell-quick:not([open])' in CSS
    assert '.hockey-map-table-picker:not([open])' in CSS


def test_rescan_preserves_links_and_only_adds_new_inventory_items(tmp_path: Path) -> None:
    database = LocalTestDatabase(tmp_path / "build75-rescan.sqlite3")
    database.create_all()
    hub = VmixAgentHub(database)  # type: ignore[arg-type]
    user = HockeyUser(id="admin-1", login="admin", display_name="Admin")

    old_inventory = {
        "fingerprint": "a" * 64,
        "inputs": [{
            "key": "score-old-key", "number": "3", "title": "SCORE BUG", "type": "GT",
            "fields": [
                {"name": "HomeScore.Text", "type": "text", "index": "0"},
                {"name": "OldOnly.Text", "type": "text", "index": "1"},
            ],
        }],
        "input_count": 1, "field_count": 2,
    }
    new_inventory = {
        "fingerprint": "b" * 64,
        "inputs": [
            {
                "key": "score-new-key", "number": "9", "title": "SCORE BUG", "type": "GT",
                "fields": [
                    {"name": "HomeScore.Text", "type": "text", "index": "4"},
                    {"name": "AwayScore.Text", "type": "text", "index": "5"},
                ],
            },
            {
                "key": "lower-third-key", "number": "10", "title": "PLAYER LOWER THIRD", "type": "GT",
                "fields": [{"name": "Name.Text", "type": "text", "index": "0"}],
            },
        ],
        "input_count": 2, "field_count": 3,
    }

    with database.session() as session:
        profile = VmixMappingProfile(
            name="KHL MAIN", description="", project_fingerprint="a" * 64,
            inventory_json=json.dumps(old_inventory), version=5, active=True,
            created_by="admin", updated_by="admin",
        )
        session.add(profile)
        session.flush()
        profile_id = profile.id
        session.add_all([
            VmixMappingField(
                profile_id=profile.id, graphic="score", data_key="game.home_score",
                vmix_input_key="score-old-key", vmix_input_number="", vmix_input_title="SCORE BUG",
                vmix_field="HomeScore.Text", field_type="text", rule_json="{}", enabled=True, sort_order=0,
            ),
            VmixMappingField(
                profile_id=profile.id, graphic="score", data_key="game.legacy",
                vmix_input_key="score-old-key", vmix_input_number="", vmix_input_title="SCORE BUG",
                vmix_field="OldOnly.Text", field_type="text", rule_json="{}", enabled=True, sort_order=1,
            ),
        ])
        session.add(VmixDevice(
            device_uuid="GFX-BUILD75", device_secret_hash="x" * 64, name="GFX",
            project_fingerprint="b" * 64, project_inventory_json=json.dumps(new_inventory),
            project_input_count=2, project_field_count=3, vmix_connected=True,
        ))

    result = asyncio.run(hub.refresh_mapping_inventory(profile_id, "GFX-BUILD75", user))
    report = result["refresh_report"]
    assert report["total_links"] == 2
    assert report["preserved"] == 1
    assert report["unresolved"] == 1
    assert report["matched_by"]["title"] == 1
    assert report["new_inputs"] == 1
    assert report["new_fields"] == 2
    assert result["inventory"]["input_count"] == 2

    with database.session() as session:
        rows = list(session.scalars(
            select(VmixMappingField)
            .where(VmixMappingField.profile_id == profile_id)
            .order_by(VmixMappingField.sort_order)
        ))
        # Existing successful link is rebound to the newly scanned Input key.
        assert rows[0].vmix_input_key == "score-new-key"
        assert rows[0].vmix_field == "HomeScore.Text"
        # Temporarily missing target is not deleted, protecting the operator's work.
        assert rows[1].vmix_input_key == "score-old-key"
        assert rows[1].vmix_field == "OldOnly.Text"
        assert len(rows) == 2


def test_rescan_can_replace_hidden_runtime_copy_of_same_config(tmp_path: Path) -> None:
    database = LocalTestDatabase(tmp_path / "build75-hidden-runtime.sqlite3")
    database.create_all()
    hub = VmixAgentHub(database)  # type: ignore[arg-type]
    user = HockeyUser(id="admin-1", login="admin", display_name="Admin")
    old_inventory = {"inputs": [{"key": "old", "number": "1", "title": "TITLE", "fields": [{"name": "Name.Text", "type": "text"}]}]}
    new_inventory = {"inputs": [{"key": "new", "number": "2", "title": "TITLE", "fields": [{"name": "Name.Text", "type": "text"}]}], "input_count": 1, "field_count": 1}

    with database.session() as session:
        source = VmixMappingProfile(
            name="SHARED", description="", project_fingerprint="a" * 64,
            inventory_json=json.dumps(old_inventory), version=1, active=True,
            created_by="admin", updated_by="admin",
        )
        session.add(source); session.flush(); source_id = source.id
        session.add(VmixMappingField(
            profile_id=source.id, graphic="lt", data_key="player.name",
            vmix_input_key="old", vmix_input_number="", vmix_input_title="TITLE",
            vmix_field="Name.Text", field_type="text", rule_json="{}", enabled=True, sort_order=0,
        ))
        runtime = VmixMappingProfile(
            name=hub._runtime_mapping_profile_name(source.id, "b" * 64),
            description="runtime", project_fingerprint="b" * 64,
            inventory_json=json.dumps(new_inventory), version=1, active=True,
            created_by="admin", updated_by="admin",
        )
        session.add(runtime); session.flush(); runtime_id = runtime.id
        session.add(VmixDevice(
            device_uuid="GFX-HIDDEN-75", device_secret_hash="x" * 64, name="GFX",
            project_fingerprint="b" * 64, project_inventory_json=json.dumps(new_inventory),
            project_input_count=1, project_field_count=1, vmix_connected=True,
        ))

    result = asyncio.run(hub.refresh_mapping_inventory(source_id, "GFX-HIDDEN-75", user))
    assert result["project_fingerprint"] == "b" * 64
    assert result["fields"][0]["vmix_input_key"] == "new"
    with database.session() as session:
        runtime = session.get(VmixMappingProfile, runtime_id)
        assert runtime.active is False
