"""Tests for web UI gated action framework (#434)."""
import sys
import unittest
from pathlib import Path
from unittest.mock import patch

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

from starlette.testclient import TestClient

from task_capability_map import TASK_CAPABILITY_MAP
from webui.app import create_app
from webui.gated_actions import (
    attempt_action,
    build_action_registry,
    load_action_registry,
    preview_action,
)


class TestGatedActionRegistry(unittest.TestCase):
    def test_all_actions_disabled_by_default(self):
        registry = build_action_registry()
        self.assertGreater(len(registry.actions), 0)
        for action in registry.actions:
            with self.subTest(action=action.action_id):
                self.assertFalse(action.enabled)

    def test_actions_align_with_task_capability_map(self):
        registry = build_action_registry()
        for action in registry.actions:
            with self.subTest(task=action.task_key):
                self.assertIn(action.task_key, TASK_CAPABILITY_MAP)
                self.assertEqual(
                    action.required_permission,
                    TASK_CAPABILITY_MAP[action.task_key]["permission"],
                )
                self.assertEqual(
                    action.required_role,
                    TASK_CAPABILITY_MAP[action.task_key]["role"],
                )

    def test_preview_includes_mutation_ledger(self):
        preview = preview_action("merge_pr", pr_number=42)
        self.assertNotIn("error", preview)
        ledger = preview["mutation_ledger"]
        self.assertEqual(len(ledger), 1)
        self.assertEqual(ledger[0]["mcp_tool"], "gitea_merge_pr")
        self.assertEqual(ledger[0]["permission"], "gitea.pr.merge")
        self.assertIn("42", ledger[0]["target"])

    def test_attempt_fails_closed_without_mcp_calls(self):
        registry = build_action_registry()
        with patch("webui.gated_actions._MVP_ACTIONS_ENABLED", False):
            for action in registry.actions:
                with self.subTest(action=action.action_id):
                    result = attempt_action(action.action_id, issue_number=1)
                    self.assertFalse(result["success"])
                    self.assertEqual(result["error"], "action_disabled")
                    self.assertIn("preview", result)

    def test_attempt_module_has_no_mcp_imports(self):
        """Ungated execution path must not import MCP server tools."""
        import webui.gated_actions as ga

        source_path = Path(ga.__file__).resolve()
        source = source_path.read_text(encoding="utf-8")
        self.assertNotIn("mcp_server", source)
        result = attempt_action("merge_pr", pr_number=99)
        self.assertFalse(result["success"])

    def test_unknown_action_fails_closed(self):
        result = attempt_action("nonexistent_action")
        self.assertFalse(result["success"])
        self.assertEqual(result["error"], "unknown_action")


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

    def test_actions_page_renders_disabled_buttons(self):
        response = self.client.get("/actions")
        self.assertEqual(response.status_code, 200)
        self.assertIn("Gated actions", response.text)
        self.assertIn("disabled", response.text.lower())
        self.assertIn("mutation ledger", response.text.lower())
        self.assertIn("aria-disabled", response.text)
        self.assertIn(" disabled", response.text)

    def test_api_actions_registry(self):
        response = self.client.get("/api/actions")
        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertFalse(data["mvp_actions_enabled"])
        action_ids = {item["action_id"] for item in data["actions"]}
        self.assertIn("merge_pr", action_ids)
        self.assertIn("claim_issue", action_ids)
        for item in data["actions"]:
            self.assertFalse(item["enabled"])

    def test_api_action_preview_json(self):
        response = self.client.get(
            "/api/actions/review_pr/preview?pr_number=12"
        )
        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertEqual(data["task_key"], "review_pr")
        self.assertEqual(data["required_role"], "reviewer")
        self.assertEqual(len(data["mutation_ledger"]), 1)

    def test_post_attempt_fails_closed(self):
        response = self.client.post(
            "/api/actions/merge_pr/attempt",
            json={"pr_number": 1},
        )
        self.assertEqual(response.status_code, 403)
        data = response.json()
        self.assertFalse(data["success"])
        self.assertEqual(data["error"], "action_disabled")

    def test_nav_includes_actions_link(self):
        text = self.client.get("/").text
        self.assertIn('href="/actions"', text)


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