"""Accepted form-submission runtime. No persistence, anti-abuse, or post-submit.

Wraps frozen 7B `FormSubmission` after 7C validation. Identity is platform
runtime (`fsb_` + 32 hex), not a row, Cloud record, CRM object, or job id.
"""
from __future__ import annotations

from collections.abc import Callable, Mapping
from datetime import UTC, datetime
from typing import Any
from uuid import uuid4

from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator

from src.domain.forms.canonical import FormDefinition, FormSubmission
from src.domain.forms.conditions import (
    FormConditionSet,
    prepare_conditional_validation,
)
from src.domain.forms.validation import (
    FILE_RUNTIME_SUPPORT,
    FormFieldError,
    FormValidationResult,
    validate_form_values,
)

SUBMISSION_ID_PREFIX = "fsb_"
SUBMISSION_ID_PATTERN = r"^fsb_[0-9a-f]{32}$"
IDEMPOTENCY = "none_identical_payloads_are_distinct"
ACCEPTANCE_MEANS = "definition_plus_7c_ok"
DEFINITION_LOOKUP = "caller_supplied_definition"
OUTCOMES = ("accepted", "rejected")


def new_submission_id() -> str:
    return f"{SUBMISSION_ID_PREFIX}{uuid4().hex}"


def platform_now() -> datetime:
    return datetime.now(UTC)


FORMS_SUBMISSION_RUNTIME: dict[str, Any] = {
    "slice_id": "7d_submission_runtime",
    "status": "frozen",
    "acceptance_means": ACCEPTANCE_MEANS,
    "definition_lookup": DEFINITION_LOOKUP,
    "submission_id_prefix": SUBMISSION_ID_PREFIX,
    "submission_id_pattern": SUBMISSION_ID_PATTERN,
    "idempotency": IDEMPOTENCY,
    "outcomes": OUTCOMES,
    "accepted_values": "7c_normalized",
    "accepted_at": "server_utc",
    "file_runtime_support": FILE_RUNTIME_SUPPORT,
    "anti_abuse": "deferred_7g",
    "persistence": False,
    "post_submit": False,
    "public_bff": False,
    "form_submit_capability": False,
    "codegen": False,
    "schema_migration": False,
    "docs": "docs/audits/FORMS_SUBMISSION_RUNTIME_7D.md",
}


class AcceptedFormSubmission(BaseModel):
    """Runtime acceptance event. Not a persisted record."""

    model_config = ConfigDict(extra="forbid", frozen=True)
    submission_id: StrictStr = Field(pattern=SUBMISSION_ID_PATTERN)
    submission: FormSubmission
    accepted_at: datetime

    @field_validator("accepted_at")
    @classmethod
    def utc_aware(cls, value: datetime) -> datetime:
        if value.tzinfo is None:
            raise ValueError("accepted_at_must_be_timezone_aware")
        return value.astimezone(UTC)


class FormSubmissionRuntimeResult(BaseModel):
    """Acceptance or 7C field errors. No status explosion."""

    model_config = ConfigDict(extra="forbid", frozen=True)
    accepted: bool
    submission: AcceptedFormSubmission | None = None
    errors: tuple[FormFieldError, ...] = ()
    validation: FormValidationResult | None = None


def accept_form_submission(
    definition: FormDefinition,
    raw_values: Mapping[str, Any],
    *,
    form_key: str | None = None,
    conditions: FormConditionSet | None = None,
    submission_id_factory: Callable[[], str] = new_submission_id,
    clock: Callable[[], datetime] = platform_now,
) -> FormSubmissionRuntimeResult:
    """Accept a payload against a supplied FormDefinition.

    Does not look up a registry, persist, or invoke downstream actions.
    """
    effective, candidate, hidden_errors = prepare_conditional_validation(
        definition, raw_values, conditions
    )
    validated = validate_form_values(effective, candidate, form_key=form_key)
    if hidden_errors or not validated.ok:
        errors = list(hidden_errors) + list(validated.errors)
        failed = FormValidationResult(
            ok=False,
            form_key=definition.key,
            values={},
            errors=errors,
        )
        return FormSubmissionRuntimeResult(
            accepted=False,
            submission=None,
            errors=tuple(failed.errors),
            validation=failed,
        )
    return FormSubmissionRuntimeResult(
        accepted=True,
        submission=AcceptedFormSubmission(
            submission_id=submission_id_factory(),
            submission=validated.submission(),
            accepted_at=clock(),
        ),
        errors=(),
        validation=validated,
    )
