"""Disabled-by-default gated action registry for future UI writes (#434).

Every action declares the MCP capability and profile role from
``task_capability_map``. Previews always render a mutation ledger; execution
is fail-closed in the read-only MVP (no MCP or shell fallbacks).
"""

from __future__ import annotations

from dataclasses import asdict, dataclass, field
from typing import Any

from task_capability_map import required_permission, required_role

# MVP: no UI action may execute mutations until capability gates ship.
_MVP_ACTIONS_ENABLED = False


@dataclass(frozen=True)
class MutationLedgerEntry:
    """One planned mutation step shown before any write executes."""

    sequence: int
    mcp_tool: str
    permission: str
    target: str
    summary: str


@dataclass(frozen=True)
class GatedAction:
    """Registry entry tying a UI action to resolver task metadata."""

    action_id: str
    label: str
    task_key: str
    mcp_tool: str
    description: str
    enabled: bool = False

    @property
    def required_permission(self) -> str:
        return required_permission(self.task_key)

    @property
    def required_role(self) -> str:
        return required_role(self.task_key)

    def mutation_ledger(self, **params: Any) -> tuple[MutationLedgerEntry, ...]:
        """Build the mutation ledger preview for *params* (read-only)."""
        target = _format_target(self.action_id, params)
        return (
            MutationLedgerEntry(
                sequence=1,
                mcp_tool=self.mcp_tool,
                permission=self.required_permission,
                target=target,
                summary=self.description,
            ),
        )

    def preview(self, **params: Any) -> dict[str, Any]:
        ledger = self.mutation_ledger(**params)
        return {
            "action_id": self.action_id,
            "label": self.label,
            "task_key": self.task_key,
            "mcp_tool": self.mcp_tool,
            "required_permission": self.required_permission,
            "required_role": self.required_role,
            "enabled": self.enabled and _MVP_ACTIONS_ENABLED,
            "mvp_mode": "read-only",
            "mutation_ledger": [asdict(entry) for entry in ledger],
            "params": dict(params),
        }

    def attempt(self, **params: Any) -> dict[str, Any]:
        """Fail closed — never invokes MCP tools in MVP."""
        preview = self.preview(**params)
        if not preview["enabled"]:
            return {
                "success": False,
                "error": "action_disabled",
                "detail": (
                    "UI actions are disabled in the read-only MVP. "
                    "Use MCP tools with capability gates."
                ),
                "preview": preview,
            }
        return {
            "success": False,
            "error": "action_not_implemented",
            "detail": "Gated execution path is not wired in MVP.",
            "preview": preview,
        }


def _format_target(action_id: str, params: dict[str, Any]) -> str:
    if action_id == "claim_issue":
        return f"issue #{params.get('issue_number', '?')}"
    if action_id in {"comment_issue", "close_issue"}:
        return f"issue #{params.get('issue_number', '?')}"
    if action_id in {"review_pr", "merge_pr", "comment_pr", "close_pr"}:
        return f"PR #{params.get('pr_number', '?')}"
    if action_id == "delete_branch":
        return f"branch {params.get('branch_name', '?')!r}"
    if action_id == "create_pr":
        return (
            f"PR {params.get('head', '?')} → {params.get('base', 'master')}"
        )
    if action_id == "create_issue":
        return f"issue {params.get('title', '?')!r}"
    return "unspecified"


@dataclass
class ActionRegistry:
    """Ordered registry of gated UI actions."""

    actions: tuple[GatedAction, ...] = field(default_factory=tuple)

    def get(self, action_id: str) -> GatedAction | None:
        for action in self.actions:
            if action.action_id == action_id:
                return action
        return None

    def to_dict(self) -> dict[str, Any]:
        return {
            "mvp_actions_enabled": _MVP_ACTIONS_ENABLED,
            "actions": [
                {
                    "action_id": action.action_id,
                    "label": action.label,
                    "task_key": action.task_key,
                    "mcp_tool": action.mcp_tool,
                    "required_permission": action.required_permission,
                    "required_role": action.required_role,
                    "enabled": action.enabled,
                    "description": action.description,
                }
                for action in self.actions
            ],
        }


def build_action_registry() -> ActionRegistry:
    """Return the canonical MVP action set (all disabled)."""
    specs = (
        ("claim_issue", "Claim issue", "claim_issue", "gitea_mark_issue",
         "Apply status:in-progress via issue comment gate."),
        ("comment_issue", "Comment on issue", "comment_issue",
         "gitea_create_issue_comment", "Post an issue comment."),
        ("create_issue", "Create issue", "create_issue", "gitea_create_issue",
         "Open a new tracking issue."),
        ("create_pr", "Create pull request", "create_pr", "gitea_create_pr",
         "Open a PR from a locked feature branch."),
        ("review_pr", "Review PR", "review_pr", "gitea_review_pr",
         "Submit approve/request-changes/comment review."),
        ("merge_pr", "Merge PR", "merge_pr", "gitea_merge_pr",
         "Merge an approved pull request."),
        ("delete_branch", "Delete branch", "delete_branch",
         "gitea_delete_branch", "Remove a remote feature branch."),
        ("comment_pr", "Comment on PR", "comment_pr",
         "gitea_create_issue_comment", "Post a PR review thread comment."),
        ("close_pr", "Close PR", "close_pr", "gitea_edit_pr",
         "Close a pull request without merge."),
    )
    actions = tuple(
        GatedAction(
            action_id=action_id,
            label=label,
            task_key=task_key,
            mcp_tool=mcp_tool,
            description=description,
            enabled=False,
        )
        for action_id, label, task_key, mcp_tool, description in specs
    )
    return ActionRegistry(actions=actions)


_REGISTRY = build_action_registry()


def load_action_registry() -> ActionRegistry:
    return _REGISTRY


def preview_action(action_id: str, **params: Any) -> dict[str, Any]:
    action = _REGISTRY.get(action_id)
    if action is None:
        return {"error": "unknown_action", "action_id": action_id}
    return action.preview(**params)


def attempt_action(action_id: str, **params: Any) -> dict[str, Any]:
    action = _REGISTRY.get(action_id)
    if action is None:
        return {"success": False, "error": "unknown_action", "action_id": action_id}
    return action.attempt(**params)