"""Consolidated MVP coverage for the internal web UI (#436)."""
from __future__ import annotations

import importlib
import os
import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from starlette.testclient import TestClient
from starlette.routing import Route

from webui.app import create_app

_REPO_ROOT = Path(__file__).resolve().parent.parent

# HTML pages and JSON exports registered on master MVP.
MVP_GET_ROUTES: tuple[tuple[str, str | tuple[str, ...]], ...] = (
    ("/", "Operator console"),
    ("/health", "mcp-control-plane-webui"),
    ("/queue", "Live queue"),
    ("/api/queue", ("prs", "issues")),
    ("/projects", "Gitea-Tools"),
    ("/api/projects", "projects"),
    ("/prompts", "Prompt library"),
    ("/api/prompts", "prompts"),
    ("/runtime", "Runtime"),
    ("/audit", "Audit"),
    ("/worktrees", "Worktrees"),
    ("/leases", "Leases"),
)

MUTATION_METHODS = ("POST", "PUT", "PATCH", "DELETE")


def _import_optional(module_name: str):
    try:
        return importlib.import_module(module_name)
    except ImportError:
        return None


class TestWebuiServerStartup(unittest.TestCase):
    def test_create_app_imports_cleanly(self):
        app = create_app()
        self.assertIsNotNone(app)

    def test_main_module_imports(self):
        import webui.__main__ as main_mod

        self.assertTrue(callable(main_mod.main))


class TestWebuiRouteRendering(unittest.TestCase):
    def setUp(self):
        self.client = TestClient(create_app())

    def test_all_mvp_get_routes_render(self):
        for path, needle in MVP_GET_ROUTES:
            with self.subTest(path=path):
                response = self.client.get(path)
                self.assertEqual(response.status_code, 200, path)
                if path == "/health":
                    assert isinstance(needle, str)
                    self.assertEqual(response.json()["service"], needle)
                elif path.startswith("/api/"):
                    data = response.json()
                    if isinstance(needle, tuple):
                        for key in needle:
                            self.assertIn(key, data)
                    else:
                        self.assertIn(needle, data)
                else:
                    assert isinstance(needle, str)
                    self.assertIn(needle, response.text)

    def test_registered_routes_include_mvp_paths(self):
        app = create_app()
        get_paths = {
            route.path
            for route in app.routes
            if isinstance(route, Route) and "GET" in route.methods
        }
        for path, _ in MVP_GET_ROUTES:
            with self.subTest(path=path):
                self.assertIn(path, get_paths)


class TestWebuiReadOnlyGuards(unittest.TestCase):
    def setUp(self):
        self.client = TestClient(create_app())

    def test_mutation_methods_rejected_on_core_paths(self):
        paths = ("/health", "/queue", "/projects", "/prompts", "/api/queue")
        for path in paths:
            for method in MUTATION_METHODS:
                with self.subTest(path=path, method=method):
                    response = self.client.request(method, path)
                    self.assertEqual(response.status_code, 405)
                    self.assertEqual(response.json()["error"], "read-only-mvp")


class TestWebuiOptionalModules(unittest.TestCase):
    """Exercise child-issue modules when present on the branch under test."""

    def test_audit_validator_basics_when_present(self):
        mod = _import_optional("webui.audit_validator")
        if mod is None:
            self.skipTest("webui.audit_validator not merged on this branch")
        report = "## Controller Handoff\n- Task: review PR #1\n"
        self.assertEqual(mod.infer_task_kind(report), "review_pr")
        result = mod.audit_report(report)
        self.assertIn("grade", result)

    def test_gated_actions_fail_closed_when_present(self):
        mod = _import_optional("webui.gated_actions")
        if mod is None:
            self.skipTest("webui.gated_actions not merged on this branch")
        registry = mod.build_action_registry()
        for action in registry.actions:
            with self.subTest(action=action.action_id):
                self.assertFalse(action.enabled)
        result = mod.attempt_action("merge_pr", pr_number=1)
        self.assertFalse(result["success"])

    def test_deployment_boundary_when_present(self):
        mod = _import_optional("webui.deployment_boundary")
        if mod is None:
            self.skipTest("webui.deployment_boundary not merged on this branch")
        assessment = mod.assess_bind_host("0.0.0.0")
        self.assertEqual(assessment.disposition, "refuse")


class TestWebuiTestInventory(unittest.TestCase):
    def test_webui_test_modules_exist(self):
        modules = sorted(_REPO_ROOT.glob("tests/test_webui_*.py"))
        names = [path.name for path in modules]
        self.assertIn("test_webui_skeleton.py", names)
        self.assertIn("test_webui_project_registry.py", names)
        self.assertIn("test_webui_prompt_library.py", names)
        self.assertIn("test_webui_queue_dashboard.py", names)
        self.assertGreaterEqual(len(names), 5)


class TestWebuiCiScripts(unittest.TestCase):
    def test_runner_scripts_exist(self):
        for name in ("test-webui", "ci-webui-check"):
            script = _REPO_ROOT / "scripts" / name
            self.assertTrue(script.is_file(), name)
            self.assertTrue(os.access(script, os.X_OK), name)


if __name__ == "__main__":
    unittest.main()