"""7B frozen Forms vocabulary. No form.* invoke verbs or runtime."""
from __future__ import annotations

from pathlib import Path

import pytest
from pydantic import ValidationError
from src.domain.app_blocks.catalogs import FEATURE_PACK_ALIASES
from src.domain.connectors.catalog import slugs_implementing
from src.domain.connectors.invoke_capabilities import (
    CAPABILITY_CONVERSION_PATTERN,
    FORMS_FAMILY_PLAN,
    FORMS_HYPOTHESIZED_VERBS_NOT_FROZEN,
    INVOKE_CAPABILITIES,
)
from src.domain.connectors.public_gateway import (
    PHASE_6_STATUS,
    PHASE_7_STATUS,
    PUBLIC_INVOCABLE_CAPABILITIES,
)
from src.domain.forms.canonical import (
    CHECKBOX_MEANS,
    FILE_RUNTIME_SUPPORT,
    FORBIDDEN_PROVIDER_TOKENS,
    FORMS_CANONICAL_MODEL,
    HIDDEN_IS_TRUSTED,
    NAMING_COLLISIONS,
    REJECTED_EQUIVALENCES,
    FormCanonicalError,
    FormDefinition,
    FormField,
    FormFieldOption,
    FormFileRef,
    FormSubmission,
    contact_block_compatible_definition,
    quote_request_block_compatible_definition,
)
from src.domain.starter_library.catalog import known_contract_ids


def _expect_code(exc: BaseException, code: str) -> None:
    if isinstance(exc, FormCanonicalError):
        assert exc.code == code
        return
    assert code in str(exc)


def test_7b_freezes_model_not_runtime() -> None:
    pattern = CAPABILITY_CONVERSION_PATTERN
    assert PHASE_6_STATUS == "6p_complete"
    assert PHASE_7_STATUS == "complete"
    assert pattern["phase_6_status"] == "6p_complete"
    assert pattern["phase_7_status"] == "complete"
    assert pattern["current_program_phase"] == 8
    assert pattern["next_program_slice"] == "9_app_block_modernization"
    assert pattern["opened_families"]["forms.*"] == "composition_integration"
    assert pattern["parked_families"] == {}
    assert FORMS_FAMILY_PLAN["object_model_frozen"] is True
    assert FORMS_FAMILY_PLAN["verbs_frozen"] is False
    assert FORMS_FAMILY_PLAN["forms_contact_is_generic_domain"] is False
    assert FORMS_FAMILY_PLAN["status"] == "complete"
    assert FORMS_FAMILY_PLAN["next_slice"] == "9_app_block_modernization"
    assert FORMS_CANONICAL_MODEL["submission_implies_persistence"] is False
    assert FORMS_CANONICAL_MODEL["file_runtime_support"] is True
    assert FORMS_CANONICAL_MODEL["runtime_validation"] == "frozen_7c"
    assert FORMS_CANONICAL_MODEL["submission_id"] == "runtime_envelope_7d"
    for verb in FORMS_HYPOTHESIZED_VERBS_NOT_FROZEN:
        assert verb not in INVOKE_CAPABILITIES
        assert verb not in PUBLIC_INVOCABLE_CAPABILITIES
        assert slugs_implementing(verb) == ()
    assert "form.submit" not in INVOKE_CAPABILITIES
    assert "form.create" not in INVOKE_CAPABILITIES


def test_naming_collisions_are_not_equivalences() -> None:
    assert NAMING_COLLISIONS["form.standard"] == "starter_ui_composite"
    assert NAMING_COLLISIONS["form.public_create"] == "composer_anonymous_create_primitive"
    assert NAMING_COLLISIONS["form.public_submission"] == (
        "pack_provide_id_aliased_to_contact_capture"
    )
    assert NAMING_COLLISIONS["form.forward"] == "cloud_http_forward_job"
    assert NAMING_COLLISIONS["forms.contact"] == "contact_form_app_block"
    assert ("form.standard", "FormDefinition") in REJECTED_EQUIVALENCES
    assert ("form.public_submission", "FormSubmission") in REJECTED_EQUIVALENCES
    assert ("form.forward", "canonical_post_submit_api") in REJECTED_EQUIVALENCES
    assert ("forms.contact", "FormDefinition") in REJECTED_EQUIVALENCES
    assert "form.standard" in known_contract_ids()
    assert FEATURE_PACK_ALIASES["forms.public_submission"] == "contact-capture"
    assert FORMS_CANONICAL_MODEL["form_standard_is_form_definition"] is False
    assert FORMS_CANONICAL_MODEL["form_public_submission_is_form_submission"] is False
    assert FORMS_CANONICAL_MODEL["auth_is_not_forms"] is True


def test_checkbox_is_boolean_not_multi_select() -> None:
    assert CHECKBOX_MEANS == "boolean"
    field = FormField(key="subscribe", type="checkbox", required=False, default=False)
    assert field.type == "checkbox"
    with pytest.raises((FormCanonicalError, ValidationError)) as exc:
        FormField(
            key="topics",
            type="checkbox",
            options=[FormFieldOption(value="news", label="News")],
        )
    _expect_code(exc.value, "options_not_allowed")
    multi = FormField(
        key="topics",
        type="multi_select",
        options=[
            FormFieldOption(value="news", label="News"),
            FormFieldOption(value="product", label="Product"),
        ],
        default=["news"],
    )
    assert multi.type == "multi_select"


def test_field_key_is_independent_of_label() -> None:
    field = FormField(key="first_name", type="text", label="Given name")
    assert field.key == "first_name"
    assert field.label == "Given name"


def test_duplicate_field_keys_rejected() -> None:
    with pytest.raises((FormCanonicalError, ValidationError)) as exc:
        FormDefinition(
            key="contact",
            fields=[
                FormField(key="email", type="email"),
                FormField(key="email", type="text"),
            ],
        )
    _expect_code(exc.value, "duplicate_field_key")


def test_option_fields_store_values_not_labels() -> None:
    definition = FormDefinition(
        key="intake",
        fields=[
            FormField(
                key="company_size",
                type="select",
                options=[
                    FormFieldOption(value="enterprise", label="Enterprise"),
                    FormFieldOption(value="smb", label="SMB"),
                ],
            )
        ],
    )
    submission = FormSubmission(
        form_key=definition.key,
        values={"company_size": "enterprise"},
    )
    assert submission.values["company_size"] == "enterprise"
    assert "Enterprise" not in submission.values.values()


def test_file_field_is_one_platform_file_ref() -> None:
    assert FILE_RUNTIME_SUPPORT is True
    field = FormField(key="attachment", type="file")
    assert field.type == "file"
    with pytest.raises((FormCanonicalError, ValidationError)) as exc:
        FormField(key="attachment", type="file", default="not-a-file-id")
    _expect_code(exc.value, "default_type_mismatch")
    submission = FormSubmission(
        form_key="quote_request",
        values={"attachment": {"file_id": "file_" + ("c" * 32)}},
    )
    assert "file_" in str(submission.values["attachment"])


def test_hidden_is_not_trusted() -> None:
    assert HIDDEN_IS_TRUSTED is False
    field = FormField(key="source", type="hidden", default="landing")
    assert field.type == "hidden"


def test_default_is_structural_only() -> None:
    FormField(key="count", type="number", default=3)
    with pytest.raises((FormCanonicalError, ValidationError)) as exc:
        FormField(key="count", type="number", default="3")
    _expect_code(exc.value, "default_type_mismatch")


def test_submission_is_not_persistence_and_rejects_documents() -> None:
    submission = FormSubmission(
        form_key="quote_request",
        values={"name": "Jane", "budget": 5000},
    )
    dumped = submission.model_dump()
    assert "id" not in dumped
    assert "record_id" not in dumped
    assert "submission_id" not in dumped
    assert FORMS_CANONICAL_MODEL["submission_id"] == "runtime_envelope_7d"
    with pytest.raises((FormCanonicalError, ValidationError)) as nested:
        FormSubmission(form_key="contact", values={"payload": {"email": "a@b.c"}})
    _expect_code(nested.value, "unsupported_submission_value")
    with pytest.raises((FormCanonicalError, ValidationError)) as reserved:
        FormSubmission(form_key="contact", values={"turnstile_token": "tok"})
    _expect_code(reserved.value, "reserved_submission_key")
    with pytest.raises(ValidationError):
        FormSubmission(form_key="contact", values={"name": "Ada"}, connection_id="conn_1")


def test_presentation_and_provider_fields_are_rejected() -> None:
    with pytest.raises(ValidationError):
        FormField(key="name", type="text", css_class="mt-4")
    with pytest.raises((FormCanonicalError, ValidationError)) as reserved:
        FormField(key="portal_id", type="text")
    _expect_code(reserved.value, "reserved_field_key")
    with pytest.raises(ValidationError):
        FormDefinition(
            key="contact",
            fields=[FormField(key="name", type="text")],
            hubspot_form_id="123",
        )


def test_forms_contact_shape_is_representable_without_migration() -> None:
    definition = contact_block_compatible_definition()
    assert definition.key == "contact"
    assert [field.key for field in definition.fields] == [
        "name",
        "email",
        "phone",
        "message",
    ]
    submission = FormSubmission(
        form_key=definition.key,
        values={
            "name": "Jane Smith",
            "email": "jane@example.com",
            "phone": "1",
            "message": "Hello",
        },
    )
    assert set(submission.values) <= {field.key for field in definition.fields}


def test_quote_request_shape_is_independent_and_has_file_field() -> None:
    definition = quote_request_block_compatible_definition()
    assert definition.key == "quote_request"
    assert definition.key != contact_block_compatible_definition().key
    attachment = next(field for field in definition.fields if field.key == "attachment")
    assert attachment.type == "file"
    assert FILE_RUNTIME_SUPPORT is True
    assert FEATURE_PACK_ALIASES["forms.public_submission"] == "contact-capture"


def test_canonical_models_have_no_provider_fields() -> None:
    for model in (FormDefinition, FormField, FormFieldOption, FormSubmission, FormFileRef):
        names = set(model.model_fields)
        for token in FORBIDDEN_PROVIDER_TOKENS:
            assert token not in names
            assert token.lower() not in {name.lower() for name in names}
    source = (
        Path(__file__).resolve().parents[1] / "src" / "domain" / "forms" / "canonical.py"
    )
    text = source.read_text(encoding="utf-8")
    for marker in ("form.submit", "/api/forms/", "storage.file.put", "form.create_user"):
        assert marker not in text
