"""Local launcher behavior, auth, dependency, and hardware contracts."""

from __future__ import annotations

import sys
from types import SimpleNamespace
from unittest.mock import Mock

import pytest

from obliteratus import local_ui


@pytest.mark.parametrize(
    ("vram", "expected"),
    [([], "cpu"), ([0], "mps"), ([4], "small"), ([8], "medium"), ([24], "large"), ([80], "frontier")],
)
def test_compute_tier_boundaries(vram, expected):
    gpus = [{"vram_gb": amount} for amount in vram]
    assert local_ui._compute_tier(gpus) == expected


def test_gpu_detection_enumerates_cuda_devices(monkeypatch):
    cuda = SimpleNamespace(
        is_available=lambda: True,
        device_count=lambda: 2,
        get_device_properties=lambda index: SimpleNamespace(
            name=f"GPU {index}",
            total_memory=(index + 8) * 1024**3,
            major=9,
            minor=index,
        ),
    )
    torch = SimpleNamespace(
        cuda=cuda,
        backends=SimpleNamespace(mps=SimpleNamespace(is_available=lambda: False)),
    )
    monkeypatch.setitem(sys.modules, "torch", torch)

    assert local_ui._detect_gpu() == [
        {"index": 0, "name": "GPU 0", "vram_gb": 8.0, "compute": "9.0"},
        {"index": 1, "name": "GPU 1", "vram_gb": 9.0, "compute": "9.1"},
    ]


def test_gpu_detection_degrades_to_cpu_when_runtime_probe_fails(monkeypatch):
    torch = SimpleNamespace(
        cuda=SimpleNamespace(is_available=Mock(side_effect=RuntimeError("driver unavailable"))),
    )
    monkeypatch.setitem(sys.modules, "torch", torch)

    assert local_ui._detect_gpu() == []


def test_launch_info_masks_password_and_reports_network_listener(monkeypatch):
    console = Mock()
    monkeypatch.setattr(local_ui, "console", console)

    local_ui._print_launch_info("0.0.0.0", 9000, True, ("operator", "secret"))

    rendered = console.print.call_args.args[0].renderable
    assert "http://localhost:9000" in rendered
    assert "http://<your-ip>:9000" in rendered
    assert "operator:******" in rendered
    assert "secret" not in rendered


def test_missing_gradio_exits_before_app_import(monkeypatch):
    console = Mock()
    monkeypatch.setattr(local_ui, "console", console)
    monkeypatch.setitem(sys.modules, "gradio", None)
    monkeypatch.delitem(sys.modules, "app", raising=False)

    with pytest.raises(SystemExit) as exc_info:
        local_ui.launch_local_ui(quiet=True)

    assert exc_info.value.code == 1
    assert "Gradio is not installed" in console.print.call_args.args[0]
    assert "app" not in sys.modules


def test_quiet_launch_skips_hardware_probes_and_forwards_server_contract(monkeypatch):
    launch = Mock()
    monkeypatch.setitem(sys.modules, "gradio", SimpleNamespace())
    monkeypatch.setitem(sys.modules, "app", SimpleNamespace(launch=launch))
    monkeypatch.setattr(local_ui, "_detect_gpu", Mock(side_effect=AssertionError("must not run")))
    monkeypatch.setattr(local_ui.sys, "path", ["/sentinel"])

    kwargs = {
        "host": "127.0.0.1",
        "port": 9999,
        "share": True,
        "open_browser": False,
        "auth": ("user", "password"),
        "quiet": True,
    }
    local_ui.launch_local_ui(**kwargs)
    local_ui.launch_local_ui(**kwargs)

    expected_root = str(local_ui.pathlib.Path(local_ui.__file__).resolve().parent.parent)
    assert local_ui.sys.path == [expected_root, "/sentinel"]
    assert launch.call_count == 2
    launch.assert_called_with(
        server_name="127.0.0.1",
        server_port=9999,
        share=True,
        inbrowser=False,
        auth=("user", "password"),
        quiet=True,
    )


def test_nonquiet_launch_reports_hardware_before_starting_app(monkeypatch):
    launch = Mock()
    gpus = [{"index": 0, "name": "GPU", "vram_gb": 24, "compute": "9.0"}]
    monkeypatch.setitem(sys.modules, "gradio", SimpleNamespace())
    monkeypatch.setitem(sys.modules, "app", SimpleNamespace(launch=launch))
    monkeypatch.setattr(local_ui, "console", Mock())
    monkeypatch.setattr(local_ui, "_detect_gpu", Mock(return_value=gpus))
    system_info = Mock()
    recommendations = Mock()
    launch_info = Mock()
    monkeypatch.setattr(local_ui, "_print_system_info", system_info)
    monkeypatch.setattr(local_ui, "_print_recommendations", recommendations)
    monkeypatch.setattr(local_ui, "_print_launch_info", launch_info)

    local_ui.launch_local_ui(host="localhost", port=7861, quiet=False)

    system_info.assert_called_once_with(gpus)
    recommendations.assert_called_once_with("large")
    launch_info.assert_called_once_with("localhost", 7861, False, None)
    launch.assert_called_once()
