"""autogovern.py — minimal Python SDK for the AutoGovern Control Plane.

No dependencies — uses only the standard library (urllib).
Docs: https://autogovern.io/governance-api

Get a free API key: create an account at https://autogovern.io/account,
register an asset of kind "agent" or "mcp_server" in AI Inventory, then
click "Activate for Control Plane".
"""
import json
import math
import re
import urllib.error
import urllib.request


class AutoGovernError(Exception):
    def __init__(self, message, result=None):
        super().__init__(message)
        self.result = result


class AutoGovernClient:
    def __init__(self, api_key, base_url="https://autogovern.io", timeout=10):
        if not api_key:
            raise ValueError("AutoGovernClient requires api_key — activate one at https://autogovern.io/account.")
        if isinstance(timeout, bool) or not isinstance(timeout, (int, float)) or not math.isfinite(timeout) or timeout <= 0:
            raise ValueError("timeout must be a positive finite number of seconds.")
        self.timeout = timeout
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")

    def evaluate(self, action, assessment_id=None):
        """Evaluate a single agent action against the Control Plane.

        action: dict with keys tool, action_type, target (optional),
            payload (optional), reversible (optional), sensitivity
            (optional), records_affected (optional). Any "agent" key you
            include is ignored — your key's registered identity is always
            authoritative, so it can't be spoofed from here.

        Returns the full decision dict: decision ("allow"|"review"|"deny"),
        riskScore, riskBand, matchedControls, matchedPolicies, reasons,
        ledger, agentIdentity, ...
        """
        body = json.dumps({"action": action, "assessment_id": assessment_id}).encode("utf-8")
        req = urllib.request.Request(
            f"{self.base_url}/api/control-plane/evaluate",
            data=body,
            method="POST",
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.api_key}",
            },
        )
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as resp:
                result = json.loads(resp.read().decode("utf-8"))
                ledger = result.get("ledger") if isinstance(result, dict) else None
                if (not isinstance(result, dict) or result.get("decision") not in ("allow", "review", "deny")
                    or result.get("authorizationProfile") != "cp-strict-2026.09"
                    or not isinstance(ledger, dict) or not ledger.get("id")
                    or type(ledger.get("seq")) is not int or ledger["seq"] <= 0
                    or any(not isinstance(ledger.get(k), str) or not re.fullmatch(r"[a-fA-F0-9]{64}", ledger[k]) for k in ("hash", "prev_hash"))
                    or not isinstance(result.get("reasons"), list)
                    or any(not isinstance(r, str) for r in result["reasons"])):
                    raise AutoGovernError("Incomplete production authorization. Do not execute.", result)
                return result
        except urllib.error.HTTPError as e:
            try:
                raw = e.read().decode("utf-8")
                payload = json.loads(raw) if raw else {}
            except (ValueError, UnicodeError, OSError):
                payload = {}
            error = payload.get("error") if isinstance(payload, dict) else None
            message = error.get("message") if isinstance(error, dict) else error
            raise AutoGovernError(message or f"AutoGovern evaluate failed (HTTP {e.code})", payload) from None
        except (OSError, ValueError, UnicodeError) as e:
            raise AutoGovernError("AutoGovern evaluation failed or timed out. Do not execute.") from e

    def assert_allowed(self, action, assessment_id=None):
        """Raises AutoGovernError unless the decision is 'allow'; otherwise
        returns the result, same shape as evaluate()."""
        result = self.evaluate(action, assessment_id)
        if result.get("decision") != "allow":
            reasons = "; ".join(result.get("reasons") or [])
            raise AutoGovernError(f"Action was {result.get('decision')}: {reasons}", result)
        return result
