"""7N Forms public BFF + runtime truth. One final submit. Not a capability."""
from __future__ import annotations

import inspect
from datetime import UTC, datetime
from pathlib import Path

from src.core.middleware import LoggingMiddleware
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_7_STATUS, PUBLIC_INVOCABLE_CAPABILITIES
from src.domain.forms.actions import (
    FormCapabilityInvokeResult,
    FormPostSubmitAction,
    FormPostSubmitPlan,
    RecordingCapabilityInvoker,
    contact_compatible_plan,
)
from src.domain.forms.anti_abuse import (
    ChallengeVerificationResult,
    FormAntiAbusePolicy,
    InMemoryAttemptLimiter,
    StaticChallengeVerifier,
)
from src.domain.forms.canonical import FormDefinition, FormField
from src.domain.forms.conditions import (
    CONDITIONAL_FIELD_NOT_ALLOWED,
    FieldConditionalRule,
    FormCondition,
    FormConditionSet,
)
from src.domain.forms.email import email_send_action
from src.domain.forms.files import FormFileRecord, InMemoryFormFileDirectory
from src.domain.forms.forward import http_forward_action
from src.domain.forms.persistence import (
    FormPersistenceContext,
    FormPersistenceError,
    InMemoryFormSubmissionStore,
    get_form_submission,
)
from src.domain.forms.public import (
    FORBIDDEN_ENVELOPE_KEYS,
    FORMS_PUBLIC_RUNTIME,
    PUBLIC_FORMS_SUBMIT_ROUTE,
    FormPublicResult,
    InMemoryFormRuntimeStore,
    ResolvedFormRuntime,
    form_runtime_is_callable,
    parse_public_submit_request,
    project_public_forms_response,
    submit_form,
)
from src.domain.forms.router import forms_router
from src.main import app

_FIXED = datetime(2026, 9, 13, 21, 0, tzinfo=UTC)
_FSB_A = "fsb_" + ("a" * 32)
_FSB_B = "fsb_" + ("b" * 32)
_FILE = "file_" + ("c" * 32)
_DASH = (
    Path(__file__).resolve().parents[2]
    / "P2HS-Dash"
    / "server"
    / "api"
    / "projects"
    / "[projectId]"
    / "forms"
    / "[formKey]"
    / "submissions.post.ts"
)
_PASS = StaticChallengeVerifier(ChallengeVerificationResult(passed=True))


class BoomStore:
    def persist(self, *args, **kwargs):
        raise FormPersistenceError("submission_persistence_failed")

    def get(self, *args, **kwargs):
        return None


def _definition() -> FormDefinition:
    return FormDefinition(
        key="contact",
        fields=[
            FormField(key="name", type="text", required=True),
            FormField(key="email", type="email", required=True),
            FormField(key="phone", type="phone", required=False),
            FormField(key="message", type="textarea", required=False),
            FormField(key="has_company", type="checkbox", required=False),
            FormField(key="company_name", type="text", required=False),
            FormField(key="subscribe", type="checkbox", required=False),
            FormField(key="resume", type="file", required=False),
        ],
    )


def _hidden_company() -> FormConditionSet:
    shown = FormCondition(field_key="has_company", operator="equals", value=True)
    return FormConditionSet(
        field_rules=(
            FieldConditionalRule(
                target_field="company_name", effect="show_when", condition=shown
            ),
        )
    )


def _runtime(**overrides) -> ResolvedFormRuntime:
    data = dict(
        definition=_definition(),
        conditions=FormConditionSet(),
        anti_abuse=FormAntiAbusePolicy(),
        plan=contact_compatible_plan(),
        persist_first=False,
    )
    data.update(overrides)
    return ResolvedFormRuntime(**data)


def _store(project_id="prj_a", runtime=None) -> InMemoryFormRuntimeStore:
    store = InMemoryFormRuntimeStore()
    store.put(project_id, runtime or _runtime())
    return store


def _submit(
    *,
    values=None,
    runtimes=None,
    invoker=None,
    submissions=None,
    files=None,
    verifier=None,
    limiter=None,
    project_id="prj_a",
    form_key="contact",
    client_identity="203.0.113.10",
    challenge=None,
    submission_id=_FSB_A,
):
    return submit_form(
        project_id=project_id,
        form_key=form_key,
        values=values
        or {
            "name": "Ada",
            "email": "ada@example.com",
            "phone": "+1 555 0100",
            "message": "Hello",
        },
        client_identity=client_identity,
        challenge=challenge,
        runtimes=runtimes or _store(),
        invoker=invoker or RecordingCapabilityInvoker(),
        submissions=submissions,
        files=files,
        verifier=verifier or _PASS,
        limiter=limiter,
        clock=lambda: _FIXED,
        submission_id_factory=lambda: submission_id,
    )


def test_7n_public_runtime_frozen() -> None:
    assert FORMS_PUBLIC_RUNTIME["status"] == "frozen"
    assert FORMS_PUBLIC_RUNTIME["public_operation"] == "final_submit_only"
    assert FORMS_PUBLIC_RUNTIME["route"] == PUBLIC_FORMS_SUBMIT_ROUTE
    assert FORMS_PUBLIC_RUNTIME["not_capability"] is True
    assert FORMS_PUBLIC_RUNTIME["form_submit_capability"] is False
    assert FORMS_PUBLIC_RUNTIME["honeypot_public"] == "fake_accepted_without_id"
    assert FORMS_PUBLIC_RUNTIME["contact_adapter"] == "unchanged"
    assert FORMS_PUBLIC_RUNTIME["codegen"] is False
    assert PHASE_7_STATUS == "complete"
    assert CAPABILITY_CONVERSION_PATTERN["phase_7_status"] == "complete"
    assert CAPABILITY_CONVERSION_PATTERN["next_program_slice"] == "9_app_block_modernization"
    assert CAPABILITY_CONVERSION_PATTERN["opened_families"]["forms.*"] == "composition_integration"
    assert FORMS_FAMILY_PLAN["status"] == "complete"
    assert FORMS_FAMILY_PLAN["public_runtime_frozen"] is True
    assert FORMS_FAMILY_PLAN["slices"][13] == "7n_public_bff_runtime_truth"
    assert FORMS_FAMILY_PLAN["next_slice"] == "9_app_block_modernization"
    assert "form.submit" not in INVOKE_CAPABILITIES
    assert "form.submit" in FORMS_HYPOTHESIZED_VERBS_NOT_FROZEN
    assert "form.submit" not in PUBLIC_INVOCABLE_CAPABILITIES
    assert FORBIDDEN_ENVELOPE_KEYS >= {"definition", "actions", "persist_first"}


def test_canonical_route_and_dash_bff_exist() -> None:
    paths = [getattr(route, "path", "") for route in app.routes]
    assert "/api/projects/{project_id}/forms/{form_key}/submissions" in paths
    assert "/api/projects/{project_id}/forms/{form_key}/files" in paths
    methods = {
        tuple(sorted(route.methods or ()))
        for route in forms_router.routes
        if getattr(route, "path", "").endswith("/submissions")
    }
    assert methods == {("POST",)}
    assert _DASH.is_file()
    dash = _DASH.read_text(encoding="utf-8")
    assert "/api/projects/${projectId}/forms/" in dash
    assert "https://core" not in dash.lower()
    codegen_root = Path("src/domain/codegen")
    joined = "\n".join(p.read_text(encoding="utf-8") for p in codegen_root.rglob("*.py"))
    assert "form.submit" not in joined
    assert "/api/forms/{form_key}/submissions" in Path(
        "src/domain/codegen/forms_codegen.py"
    ).read_text(encoding="utf-8")
    contact = Path("src/domain/public/router.py").read_text(encoding="utf-8")
    assert "/api/public/contact" in contact


def test_runtime_truth_requires_trusted_definition() -> None:
    empty = InMemoryFormRuntimeStore()
    assert form_runtime_is_callable(empty, "prj_a", "contact") is False
    bad = _runtime(
        plan=FormPostSubmitPlan(
            actions=(
                FormPostSubmitAction(
                    key="sync_crm",
                    action_type="crm.contact.upsert",
                    mapping={"email": "missing_field"},
                ),
            )
        )
    )
    store = _store(runtime=bad)
    assert form_runtime_is_callable(store, "prj_a", "contact") is False
    result = _submit(runtimes=store)
    assert result.http_status == 503
    assert result.body == {"ok": False, "error": "form_unavailable"}
    missing = _submit(runtimes=empty)
    assert missing.http_status == 404
    assert missing.body["error"] == "form_unavailable"
    assert "app block" not in str(missing.body).lower()


def test_valid_submit_projects_fsb_without_provider_data() -> None:
    invoker = RecordingCapabilityInvoker(
        {
            "crm.contact.upsert": FormCapabilityInvokeResult(
                status="failed",
                error_code="binding_ambiguous",
                execution_ref="hs_999",
            ),
            "email.send": FormCapabilityInvokeResult(
                status="succeeded", execution_ref="re_msg"
            ),
            "automation.workflow.trigger": FormCapabilityInvokeResult(
                status="failed", error_code="workflow_unbound"
            ),
        }
    )
    store = InMemoryFormSubmissionStore()
    result = _submit(
        invoker=invoker,
        submissions=store,
        runtimes=_store(runtime=_runtime(persist_first=True)),
    )
    assert result.http_status == 200
    assert result.body == {
        "ok": True,
        "status": "accepted",
        "submission_id": _FSB_A,
    }
    assert "binding_ambiguous" not in str(result.body)
    assert "hs_999" not in str(result.body)
    assert "workflow_unbound" not in str(result.body)
    assert "binding_ambiguous" in result.internal_action_error_codes
    assert get_form_submission(store, FormPersistenceContext(project_id="prj_a"), _FSB_A)
    assert {call["capability"] for call in invoker.calls} >= {
        "crm.contact.upsert",
        "email.send",
        "automation.workflow.trigger",
    }


def test_duplicate_posts_mint_two_fsb() -> None:
    ids = iter((_FSB_A, _FSB_B))
    runtimes = _store()
    invoker = RecordingCapabilityInvoker()
    kwargs = dict(
        project_id="prj_a",
        form_key="contact",
        values={"name": "Ada", "email": "ada@example.com", "message": "Hi"},
        client_identity="ip",
        runtimes=runtimes,
        invoker=invoker,
        verifier=_PASS,
        clock=lambda: _FIXED,
        submission_id_factory=lambda: next(ids),
    )
    first = submit_form(**kwargs)
    second = submit_form(**kwargs)
    assert first.body["submission_id"] == _FSB_A
    assert second.body["submission_id"] == _FSB_B


def test_validation_aggregation_and_no_fsb() -> None:
    store = InMemoryFormSubmissionStore()
    invoker = RecordingCapabilityInvoker()
    result = _submit(
        values={"name": "", "email": "not-an-email", "subscribe": "on"},
        submissions=store,
        invoker=invoker,
    )
    assert result.http_status == 422
    assert result.body["error"] == "validation_failed"
    fields = result.body["fields"]
    assert "name" in fields and "email" in fields and "subscribe" in fields
    assert fields["subscribe"] == ["invalid_type"]
    assert result.submission_id is None
    assert invoker.calls == []
    assert get_form_submission(store, FormPersistenceContext(project_id="prj_a"), _FSB_A) is None


def test_conditional_stale_field_survives_publicly() -> None:
    result = _submit(
        values={
            "name": "Ada",
            "email": "ada@example.com",
            "company_name": "Acme",
        },
        runtimes=_store(runtime=_runtime(conditions=_hidden_company())),
        invoker=RecordingCapabilityInvoker(),
    )
    assert result.http_status == 422
    assert result.body["fields"]["company_name"] == [CONDITIONAL_FIELD_NOT_ALLOWED]
    assert result.submission_id is None


def test_invalid_file_is_field_addressable() -> None:
    files = InMemoryFormFileDirectory()
    files.put(FormFileRecord(project_id="prj_b", file_id=_FILE))
    result = _submit(
        values={
            "name": "Ada",
            "email": "ada@example.com",
            "resume": {"file_id": _FILE},
        },
        files=files,
        invoker=RecordingCapabilityInvoker(),
    )
    assert result.http_status == 422
    assert result.body["fields"]["resume"] == ["invalid_file"]
    assert result.submission_id is None


def test_honeypot_fakes_success_without_fsb_or_actions() -> None:
    invoker = RecordingCapabilityInvoker()
    store = InMemoryFormSubmissionStore()
    result = _submit(
        values={
            "name": "Ada",
            "email": "ada@example.com",
            "website": "http://spam.example",
        },
        invoker=invoker,
        submissions=store,
    )
    assert result.http_status == 200
    assert result.body == {"ok": True, "status": "accepted"}
    assert "submission_id" not in result.body
    assert result.honeypot_triggered is True
    assert result.submission_id is None
    assert invoker.calls == []
    assert "honeypot" not in str(result.body)


def test_challenge_and_rate_limit_public_errors() -> None:
    challenged = _submit(
        runtimes=_store(
            runtime=_runtime(anti_abuse=FormAntiAbusePolicy(challenge_required=True))
        ),
        verifier=StaticChallengeVerifier(
            ChallengeVerificationResult(passed=False, failure_code="challenge_failed")
        ),
        challenge="token",
        invoker=RecordingCapabilityInvoker(),
    )
    assert challenged.http_status == 400
    assert challenged.body["error"] == "challenge_failed"
    assert "turnstile" not in str(challenged.body).lower()
    limiter = InMemoryAttemptLimiter()
    policy = FormAntiAbusePolicy(rate_limit_max_attempts=1)
    first = _submit(
        runtimes=_store(runtime=_runtime(anti_abuse=policy)),
        limiter=limiter,
        invoker=RecordingCapabilityInvoker(),
    )
    second = _submit(
        runtimes=_store(runtime=_runtime(anti_abuse=policy)),
        limiter=limiter,
        invoker=RecordingCapabilityInvoker(),
        submission_id=_FSB_B,
    )
    assert first.http_status == 200
    assert second.http_status == 429
    assert second.body == {"ok": False, "error": "rate_limited"}
    assert "prj_a" not in str(second.body)


def test_persist_first_failure_is_not_public_accepted() -> None:
    invoker = RecordingCapabilityInvoker()
    result = _submit(
        runtimes=_store(runtime=_runtime(persist_first=True)),
        submissions=BoomStore(),
        invoker=invoker,
    )
    assert result.http_status == 500
    assert result.body == {"ok": False, "error": "submission_failed"}
    assert result.submission_id == _FSB_A
    assert invoker.calls == []


def test_forward_failure_still_accepted() -> None:
    invoker = RecordingCapabilityInvoker(
        {
            "internal.http_forward": FormCapabilityInvokeResult(
                status="failed",
                error_code="forward_failed",
                execution_ref="https://secret.example/hook",
            )
        }
    )
    result = _submit(
        runtimes=_store(
            runtime=_runtime(
                plan=FormPostSubmitPlan(
                    actions=(
                        http_forward_action(target="https://example.com/webhook"),
                    )
                )
            )
        ),
        invoker=invoker,
    )
    assert result.body["status"] == "accepted"
    assert "forward_failed" not in str(result.body)
    assert "secret.example" not in str(result.body)
    assert "forward_failed" in result.internal_action_error_codes


def test_project_isolation_and_rejected_envelope() -> None:
    a = _runtime(
        definition=FormDefinition(
            key="contact",
            fields=[FormField(key="email", type="email", required=True)],
        ),
        plan=FormPostSubmitPlan(
            actions=(
                email_send_action(
                    mapping={"to": "email", "text": "email"},
                    constants={"subject": "Hi"},
                ),
            )
        ),
    )
    b = _runtime(
        definition=FormDefinition(
            key="contact",
            fields=[
                FormField(key="email", type="email", required=True),
                FormField(key="name", type="text", required=True),
            ],
        ),
        plan=FormPostSubmitPlan(),
    )
    store = InMemoryFormRuntimeStore()
    store.put("prj_a", a)
    store.put("prj_b", b)
    only_email = _submit(
        project_id="prj_a",
        values={"email": "a@example.com"},
        runtimes=store,
        invoker=RecordingCapabilityInvoker(),
    )
    missing_name = _submit(
        project_id="prj_b",
        values={"email": "b@example.com"},
        runtimes=store,
        invoker=RecordingCapabilityInvoker(),
        submission_id=_FSB_B,
    )
    assert only_email.http_status == 200
    assert missing_name.http_status == 422
    assert "name" in missing_name.body["fields"]
    parsed = parse_public_submit_request(
        {
            "values": {"email": "x@example.com"},
            "actions": ["crm.contact.upsert"],
            "persist_first": False,
        }
    )
    assert isinstance(parsed, FormPublicResult)
    assert parsed.body["error"] == "invalid_request"
    leaked = project_public_forms_response(
        ok=True, http_status=200, submission_id=_FSB_A
    )
    assert "provider" not in leaked
    assert "execution_ref" not in leaked


def test_logging_does_not_emit_bodies() -> None:
    src = inspect.getsource(LoggingMiddleware.dispatch)
    assert "request.body" not in src
    assert "json()" not in src
    assert "challenge" not in src
    assert "request_completed" in src
