"""Event envelope helpers and validation."""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any
from uuid import UUID, uuid4

ALLOWED_SCOPES = {"private", "shared", "public"}
AMCS_VERSION = "AMCS-0.1"
REQUIRED_FIELDS = (
    "amcs_version",
    "event_id",
    "agent_id",
    "sequence",
    "event_type",
    "timestamp",
    "scope",
    "payload",
    "tags",
)


class EventValidationError(ValueError):
    """Raised when an event envelope is invalid."""


def _utc_now_iso() -> str:
    return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")


def _validate_uuid4(value: str, *, field_name: str) -> None:
    try:
        parsed = UUID(value)
    except ValueError as exc:
        raise EventValidationError(f"{field_name} must be a valid UUID") from exc
    if parsed.version != 4:
        raise EventValidationError(f"{field_name} must be a UUIDv4")


def _validate_rfc3339_utc(value: str, *, field_name: str) -> None:
    try:
        parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
    except ValueError as exc:
        raise EventValidationError(f"{field_name} must be a valid RFC3339 timestamp") from exc
    if parsed.tzinfo is None:
        raise EventValidationError(f"{field_name} must include timezone")


def build_event_envelope(
    *,
    agent_id: str,
    event_type: str,
    payload: dict[str, Any],
    scope: str = "private",
    tags: list[str] | None = None,
    timestamp: str | None = None,
    event_id: str | None = None,
    sequence: int | None = None,
) -> dict[str, Any]:
    """Build a standard event envelope with safe defaults."""
    event = {
        "amcs_version": AMCS_VERSION,
        "event_id": event_id or str(uuid4()),
        "agent_id": agent_id,
        "sequence": sequence,
        "event_type": event_type,
        "timestamp": timestamp or _utc_now_iso(),
        "scope": scope,
        "payload": payload,
        "tags": tags or [],
    }
    validate_event_envelope(event)
    return event


def validate_event_envelope(event: dict[str, Any]) -> None:
    """Validate required fields and type/shape constraints for an event envelope."""
    if not isinstance(event, dict):
        raise EventValidationError("Event envelope must be a dictionary")

    missing = [field for field in REQUIRED_FIELDS if field not in event]
    if missing:
        raise EventValidationError(f"Missing required field(s): {', '.join(missing)}")

    if event["amcs_version"] != AMCS_VERSION:
        raise EventValidationError(f"amcs_version must be exactly {AMCS_VERSION}")
    if not isinstance(event["event_id"], str) or not event["event_id"]:
        raise EventValidationError("event_id must be a non-empty string")
    _validate_uuid4(event["event_id"], field_name="event_id")
    if not isinstance(event["agent_id"], str) or not event["agent_id"]:
        raise EventValidationError("agent_id must be a non-empty string")
    if event["sequence"] is not None and (
        not isinstance(event["sequence"], int) or event["sequence"] < 1
    ):
        raise EventValidationError("sequence must be None or a positive integer")
    if not isinstance(event["event_type"], str) or not event["event_type"]:
        raise EventValidationError("event_type must be a non-empty string")
    if not isinstance(event["timestamp"], str) or not event["timestamp"]:
        raise EventValidationError("timestamp must be a non-empty string")
    _validate_rfc3339_utc(event["timestamp"], field_name="timestamp")

    scope = event["scope"]
    if not isinstance(scope, str) or scope not in ALLOWED_SCOPES:
        allowed = ", ".join(sorted(ALLOWED_SCOPES))
        raise EventValidationError(f"scope must be one of: {allowed}")

    if not isinstance(event["payload"], dict):
        raise EventValidationError("payload must be a dictionary")

    tags = event["tags"]
    if not isinstance(tags, list) or any(not isinstance(tag, str) for tag in tags):
        raise EventValidationError("tags must be a list of strings")
