from __future__ import annotations

import hashlib
import json
from typing import Any, Mapping


DEFAULT_POLICY = {
    "policy_key": "invoice_create_default",
    "policy_version": 2,
}


def _canonical_json(value: Any) -> str:
    return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))


def _trace_hash(payload: Mapping[str, Any], *, domain: str = "kernite.execute.v1") -> str:
    canonical = _canonical_json(payload)
    digest = hashlib.sha256(f"{domain}\n{canonical}".encode("utf-8")).hexdigest()
    return f"sha256:{digest}"


def _required_currency_reason(
    object_type: str,
    operation: str,
    payload: Mapping[str, Any],
) -> dict[str, Any] | None:
    if object_type != "invoice" or operation != "create":
        return None

    currency = payload.get("currency")
    if currency in (None, "", []):
        return {
            "code": "required_currency",
            "message": "currency is required.",
            "field_path": "currency",
        }
    return None


def evaluate_execute(
    request_body: Mapping[str, Any],
    *,
    idempotency_key: str | None = None,
) -> dict[str, Any]:
    workspace_id = str(request_body.get("workspace_id") or "").strip()
    object_type = str(request_body.get("object_type") or "").strip().lower()
    operation = str(request_body.get("operation") or "").strip().lower()
    principal = request_body.get("principal")
    payload = request_body.get("payload") or {}

    if not isinstance(payload, Mapping):
        payload = {}
    if not isinstance(principal, Mapping):
        principal = {}

    normalized = {
        "workspace_id": workspace_id,
        "principal": {
            "type": str(principal.get("type") or "").strip().lower(),
            "id": str(principal.get("id") or "").strip(),
        },
        "object_type": object_type,
        "operation": operation,
        "payload": payload,
        "idempotency_key": idempotency_key,
    }

    reasons: list[dict[str, Any]] = []
    required_currency = _required_currency_reason(object_type, operation, payload)
    if required_currency:
        reasons.append(required_currency)

    decision = "denied" if reasons else "approved"
    reason_codes = [reason["code"] for reason in reasons]
    trace_hash = _trace_hash(
        {
            "input": normalized,
            "decision": decision,
            "reason_codes": reason_codes,
        }
    )

    ctx_id = f"ctx_{trace_hash.replace('sha256:', '')[:12]}"
    message = (
        "Denied by governance policy." if decision == "denied" else "Approved by governance policy."
    )

    return {
        "ctx_id": ctx_id,
        "message": message,
        "data": {
            "decision": decision,
            "reason_codes": reason_codes,
            "reasons": reasons,
            "policy_selection_reason_code": "policy_selected_workspace_default",
            "policy": dict(DEFAULT_POLICY),
            "trace_hash": trace_hash,
            "idempotency_key": idempotency_key,
        },
    }
