from dataclasses import dataclass


@dataclass(frozen=True)
class AuthorizationDecision:
    allowed: bool
    reason: str
    required_capabilities: tuple[str, ...]
    granted_capabilities: tuple[str, ...]


ACTION_POLICY = {
    "runtime.inject_yaml": ("runtime.write",),
    "agents.inject": ("runtime.write", "hardware.write", "software.write"),
    "agents.inject_pending": ("runtime.write",),
    "agents.remove": ("runtime.write",),
    "agents.input": ("runtime.write",),
    "tasks.reinject": ("runtime.write",),
    "tasks.delete": ("runtime.write",),
    "proposals.validate": ("intelligence.write",),
    "proposals.inject": ("intelligence.write",),
    "proposals.archive": ("intelligence.write",),
    "control.freeze_zone": ("runtime.control",),
    "control.unfreeze_zone": ("runtime.control",),
    "control.kill_zone": ("runtime.control",),
    "control.pause_core": ("runtime.control",),
    "control.resume_core": ("runtime.control",),
    "control.shutdown_core": ("runtime.control",),
    "control.set_tickrate": ("runtime.control",),
    "control.step_tick": ("runtime.control",),
    "sessions.create": ("runtime.session",),
    "sessions.switch": ("runtime.session",),
    "sessions.delete": ("runtime.session",),
    "terminal.open": ("terminal.stream",),
    "terminal.close": ("terminal.stream",),
    "terminal.clear": ("terminal.stream",),
    "terminal.transform": ("terminal.stream",),
    "terminal.exec": ("terminal.exec",),
    "intent.preview": ("intelligence.write",),
    "intent.apply": ("intelligence.write",),
}


def parse_capabilities(raw):
    if raw is None:
        return []
    if isinstance(raw, list):
        return [str(item).strip() for item in raw if str(item).strip()]
    return [item.strip() for item in str(raw).split(",") if item.strip()]


def authorize_action(action, granted_capabilities, enforce=False):
    required = ACTION_POLICY.get(action)
    granted = tuple(parse_capabilities(granted_capabilities))
    if required is None:
        return AuthorizationDecision(
            allowed=False,
            reason="policy_action_unregistered",
            required_capabilities=(),
            granted_capabilities=granted,
        )

    if not enforce and not granted:
        return AuthorizationDecision(
            allowed=True,
            reason="capability_enforcement_disabled",
            required_capabilities=tuple(required),
            granted_capabilities=granted,
        )

    if _has_any_capability(granted, required):
        return AuthorizationDecision(
            allowed=True,
            reason="authorized",
            required_capabilities=tuple(required),
            granted_capabilities=granted,
        )
    return AuthorizationDecision(
        allowed=False,
        reason="capability_denied",
        required_capabilities=tuple(required),
        granted_capabilities=granted,
    )


def _has_any_capability(granted, required):
    return any(_matches_required(grant, req) for grant in granted for req in required)


def _matches_required(granted_capability, required_capability):
    granted = str(granted_capability).strip()
    required = str(required_capability).strip()
    if granted in {"*", "admin"}:
        return True
    if granted == required:
        return True
    if granted.endswith(".*"):
        prefix = granted[:-2]
        return required == prefix or required.startswith(prefix + ".")
    return False
