"""Policy Engine (build plan Section 7.1).

Every proposed tool call passes through here before execution:

1. Default-deny anything outside the agent's allow-list.
2. For sensitive tools, consult the workspace approval rules — a tool may be
   explicitly denied, auto-approved (allowed without a human), or (the default)
   require human approval.

Outcomes are ``allow`` / ``deny`` / ``require_approval``; every evaluation of a
sensitive tool is written to the audit log by the caller.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum

from legate.agent.tooling import ToolSpec


class PolicyEffect(str, Enum):
    ALLOW = "allow"
    DENY = "deny"
    REQUIRE_APPROVAL = "require_approval"


@dataclass
class PolicyDecision:
    effect: PolicyEffect
    reason: str = ""
    auto_approved: bool = False

    @property
    def allowed(self) -> bool:
        return self.effect is PolicyEffect.ALLOW


@dataclass
class ApprovalRules:
    """Per-workspace rules, read from ``workspace.settings_json['approval']``."""

    auto_approve: list[str] = field(default_factory=list)
    deny: list[str] = field(default_factory=list)

    @classmethod
    def from_settings(cls, settings_json: dict | None) -> ApprovalRules:
        rules = (settings_json or {}).get("approval", {})
        return cls(
            auto_approve=list(rules.get("auto_approve", [])),
            deny=list(rules.get("deny", [])),
        )


def evaluate_tool_call(
    *, tool: ToolSpec, allowed_tools: list[str], rules: ApprovalRules | None = None
) -> PolicyDecision:
    rules = rules or ApprovalRules()

    if tool.name not in allowed_tools:
        return PolicyDecision(
            PolicyEffect.DENY, f"tool '{tool.name}' is not in the agent allow-list"
        )
    if not tool.is_sensitive:
        return PolicyDecision(PolicyEffect.ALLOW)

    # Sensitive tool: workspace rules decide.
    if tool.name in rules.deny:
        return PolicyDecision(
            PolicyEffect.DENY, f"tool '{tool.name}' is denied by workspace policy"
        )
    if tool.name in rules.auto_approve:
        return PolicyDecision(
            PolicyEffect.ALLOW,
            f"tool '{tool.name}' is auto-approved by workspace policy",
            auto_approved=True,
        )
    return PolicyDecision(
        PolicyEffect.REQUIRE_APPROVAL,
        f"tool '{tool.name}' is sensitive and requires approval",
    )
