from __future__ import annotations

import uuid
from typing import Any, Mapping, Sequence


ALLOWED_OPERATIONS = {"create", "update", "archive", "activate", "delete"}


def _ctx_id() -> str:
    return f"ctx_{uuid.uuid4().hex[:12]}"


def _normalize_optional_string(value: Any, *, lower: bool = False) -> str:
    normalized = str(value or "").strip()
    if lower:
        normalized = normalized.lower()
    return normalized


def _is_sequence(value: Any) -> bool:
    return isinstance(value, Sequence) and not isinstance(
        value, (str, bytes, bytearray)
    )


def _validate_policy_context(
    policy_context: Mapping[str, Any],
    *,
    errors: list[dict[str, str]],
) -> dict[str, Any]:
    normalized: dict[str, Any] = {}

    governed = policy_context.get("governed")
    if governed is not None:
        if isinstance(governed, bool):
            normalized["governed"] = governed
        else:
            errors.append(
                {
                    "field": "policy_context.governed",
                    "code": "invalid_type",
                    "message": "policy_context.governed must be a boolean.",
                }
            )

    policy_selection_reason_code = policy_context.get("policy_selection_reason_code")
    if policy_selection_reason_code is not None:
        normalized_reason_code = _normalize_optional_string(
            policy_selection_reason_code
        )
        if not normalized_reason_code:
            errors.append(
                {
                    "field": "policy_context.policy_selection_reason_code",
                    "code": "invalid_value",
                    "message": "policy_context.policy_selection_reason_code must be a non-empty string.",
                }
            )
        else:
            normalized["policy_selection_reason_code"] = normalized_reason_code

    governed_scopes = policy_context.get("governed_scopes")
    if governed_scopes is not None:
        if not _is_sequence(governed_scopes):
            errors.append(
                {
                    "field": "policy_context.governed_scopes",
                    "code": "invalid_type",
                    "message": "policy_context.governed_scopes must be an array.",
                }
            )
        else:
            normalized_scopes: list[dict[str, str]] = []
            for index, scope in enumerate(governed_scopes):
                field_base = f"policy_context.governed_scopes[{index}]"
                if not isinstance(scope, Mapping):
                    errors.append(
                        {
                            "field": field_base,
                            "code": "invalid_type",
                            "message": f"{field_base} must be an object.",
                        }
                    )
                    continue

                object_type = _normalize_optional_string(
                    scope.get("object_type"), lower=True
                )
                operation = _normalize_optional_string(
                    scope.get("operation"), lower=True
                )
                if not object_type or not operation:
                    errors.append(
                        {
                            "field": field_base,
                            "code": "invalid_value",
                            "message": f"{field_base} must include object_type and operation.",
                        }
                    )
                    continue

                normalized_scopes.append(
                    {
                        "object_type": object_type,
                        "operation": operation,
                    }
                )

            normalized["governed_scopes"] = normalized_scopes

    selected_policies = policy_context.get("selected_policies")
    if selected_policies is not None:
        if not _is_sequence(selected_policies):
            errors.append(
                {
                    "field": "policy_context.selected_policies",
                    "code": "invalid_type",
                    "message": "policy_context.selected_policies must be an array.",
                }
            )
        else:
            normalized_policies: list[dict[str, Any]] = []
            for policy_index, policy in enumerate(selected_policies):
                field_base = f"policy_context.selected_policies[{policy_index}]"
                if not isinstance(policy, Mapping):
                    errors.append(
                        {
                            "field": field_base,
                            "code": "invalid_type",
                            "message": f"{field_base} must be an object.",
                        }
                    )
                    continue

                normalized_policy: dict[str, Any] = {}

                policy_key = policy.get("policy_key")
                if policy_key is not None:
                    normalized_policy_key = _normalize_optional_string(policy_key)
                    if not normalized_policy_key:
                        errors.append(
                            {
                                "field": f"{field_base}.policy_key",
                                "code": "invalid_value",
                                "message": f"{field_base}.policy_key must be a non-empty string.",
                            }
                        )
                    else:
                        normalized_policy["policy_key"] = normalized_policy_key

                effect = policy.get("effect")
                if effect is not None:
                    normalized_effect = _normalize_optional_string(effect, lower=True)
                    if normalized_effect not in {"allow", "deny"}:
                        errors.append(
                            {
                                "field": f"{field_base}.effect",
                                "code": "invalid_value",
                                "message": f"{field_base}.effect must be one of: allow, deny.",
                            }
                        )
                    else:
                        normalized_policy["effect"] = normalized_effect

                for numeric_key in ("policy_version", "version", "priority"):
                    raw_numeric = policy.get(numeric_key)
                    if raw_numeric is None:
                        continue
                    try:
                        normalized_policy[numeric_key] = int(raw_numeric)
                    except (TypeError, ValueError):
                        errors.append(
                            {
                                "field": f"{field_base}.{numeric_key}",
                                "code": "invalid_type",
                                "message": f"{field_base}.{numeric_key} must be an integer.",
                            }
                        )

                for string_key in ("policy_id", "policy_version_id", "policy_name"):
                    raw_string = policy.get(string_key)
                    if raw_string is None:
                        continue
                    normalized_string = _normalize_optional_string(raw_string)
                    if normalized_string:
                        normalized_policy[string_key] = normalized_string

                raw_rules = policy.get("rules")
                if raw_rules is not None:
                    if not _is_sequence(raw_rules):
                        errors.append(
                            {
                                "field": f"{field_base}.rules",
                                "code": "invalid_type",
                                "message": f"{field_base}.rules must be an array.",
                            }
                        )
                    else:
                        normalized_rules: list[dict[str, Any]] = []
                        for rule_index, rule in enumerate(raw_rules):
                            rule_field = f"{field_base}.rules[{rule_index}]"
                            if not isinstance(rule, Mapping):
                                errors.append(
                                    {
                                        "field": rule_field,
                                        "code": "invalid_type",
                                        "message": f"{rule_field} must be an object.",
                                    }
                                )
                                continue
                            normalized_rules.append(dict(rule))
                        normalized_policy["rules"] = normalized_rules

                normalized_policies.append(normalized_policy)

            normalized["selected_policies"] = normalized_policies

    default_policy = policy_context.get("default_policy")
    if default_policy is not None:
        if not isinstance(default_policy, Mapping):
            errors.append(
                {
                    "field": "policy_context.default_policy",
                    "code": "invalid_type",
                    "message": "policy_context.default_policy must be an object.",
                }
            )
        else:
            normalized["default_policy"] = dict(default_policy)

    validation_rules = policy_context.get("validation_rules")
    if validation_rules is not None:
        if not _is_sequence(validation_rules):
            errors.append(
                {
                    "field": "policy_context.validation_rules",
                    "code": "invalid_type",
                    "message": "policy_context.validation_rules must be an array.",
                }
            )
        else:
            normalized_rules: list[dict[str, Any]] = []
            for index, rule in enumerate(validation_rules):
                field_base = f"policy_context.validation_rules[{index}]"
                if not isinstance(rule, Mapping):
                    errors.append(
                        {
                            "field": field_base,
                            "code": "invalid_type",
                            "message": f"{field_base} must be an object.",
                        }
                    )
                    continue
                normalized_rules.append(dict(rule))
            normalized["validation_rules"] = normalized_rules

    guardrail_result = policy_context.get("guardrail_result")
    if guardrail_result is not None:
        if not isinstance(guardrail_result, Mapping):
            errors.append(
                {
                    "field": "policy_context.guardrail_result",
                    "code": "invalid_type",
                    "message": "policy_context.guardrail_result must be an object.",
                }
            )
        else:
            normalized["guardrail_result"] = dict(guardrail_result)

    no_policy_decision = policy_context.get("no_policy_decision")
    if no_policy_decision is not None:
        normalized_decision = _normalize_optional_string(no_policy_decision, lower=True)
        if normalized_decision not in {"approved", "denied"}:
            errors.append(
                {
                    "field": "policy_context.no_policy_decision",
                    "code": "invalid_value",
                    "message": "policy_context.no_policy_decision must be one of: approved, denied.",
                }
            )
        else:
            normalized["no_policy_decision"] = normalized_decision

    for key in (
        "no_policy_reason_code",
        "no_policy_message",
        "previous_trace_hash",
        "combining_algorithm",
        "trace_domain",
    ):
        raw_value = policy_context.get(key)
        if raw_value is None:
            continue
        normalized_value = _normalize_optional_string(raw_value)
        if not normalized_value:
            errors.append(
                {
                    "field": f"policy_context.{key}",
                    "code": "invalid_value",
                    "message": f"policy_context.{key} must be a non-empty string.",
                }
            )
            continue
        normalized[key] = normalized_value

    return normalized


def validate_execute_request(
    request_body: Any,
    *,
    idempotency_key: str | None = None,
) -> dict[str, Any]:
    errors: list[dict[str, str]] = []
    body: Mapping[str, Any] = {}

    if isinstance(request_body, Mapping):
        body = request_body
    else:
        errors.append(
            {
                "field": "body",
                "code": "invalid_type",
                "message": "Request body must be an object.",
            }
        )

    workspace_id = _normalize_optional_string(body.get("workspace_id"))
    object_type = _normalize_optional_string(body.get("object_type"), lower=True)
    operation = _normalize_optional_string(body.get("operation"), lower=True)
    payload = body.get("payload")
    principal = body.get("principal")
    policy_context = body.get("policy_context")

    if not workspace_id:
        errors.append(
            {
                "field": "workspace_id",
                "code": "required",
                "message": "workspace_id is required.",
            }
        )

    if not object_type:
        errors.append(
            {
                "field": "object_type",
                "code": "required",
                "message": "object_type is required.",
            }
        )

    if not operation:
        errors.append(
            {
                "field": "operation",
                "code": "required",
                "message": "operation is required.",
            }
        )
    elif operation not in ALLOWED_OPERATIONS:
        errors.append(
            {
                "field": "operation",
                "code": "invalid_value",
                "message": "operation must be one of: "
                + ", ".join(sorted(ALLOWED_OPERATIONS)),
            }
        )

    if not isinstance(payload, Mapping):
        errors.append(
            {
                "field": "payload",
                "code": "invalid_type",
                "message": "payload must be an object.",
            }
        )

    principal_type = ""
    principal_id = ""
    if not isinstance(principal, Mapping):
        errors.append(
            {
                "field": "principal",
                "code": "invalid_type",
                "message": "principal must be an object.",
            }
        )
    else:
        principal_type = _normalize_optional_string(principal.get("type"), lower=True)
        principal_id = _normalize_optional_string(principal.get("id"))
        if not principal_type:
            errors.append(
                {
                    "field": "principal.type",
                    "code": "required",
                    "message": "principal.type is required.",
                }
            )
        if not principal_id:
            errors.append(
                {
                    "field": "principal.id",
                    "code": "required",
                    "message": "principal.id is required.",
                }
            )

    normalized_policy_context: dict[str, Any] | None = None
    if policy_context is not None:
        if not isinstance(policy_context, Mapping):
            errors.append(
                {
                    "field": "policy_context",
                    "code": "invalid_type",
                    "message": "policy_context must be an object.",
                }
            )
        else:
            normalized_policy_context = _validate_policy_context(
                policy_context,
                errors=errors,
            )

    valid = len(errors) == 0
    normalized_payload: dict[str, Any] = {
        "workspace_id": workspace_id,
        "principal": {
            "type": principal_type,
            "id": principal_id,
        },
        "object_type": object_type,
        "operation": operation,
        "payload": dict(payload or {}) if isinstance(payload, Mapping) else {},
        "idempotency_key": idempotency_key,
    }
    if normalized_policy_context is not None:
        normalized_payload["policy_context"] = normalized_policy_context

    return {
        "ctx_id": _ctx_id(),
        "message": (
            "Execute payload is valid." if valid else "Execute payload is invalid."
        ),
        "data": {
            "valid": valid,
            "errors": errors,
            "normalized": normalized_payload,
        },
    }
