"""7I Forms CRM composition: crm.contact.upsert only, no resolver."""
from __future__ import annotations

from datetime import UTC, datetime
from pathlib import Path

from src.domain.connectors.invoke_capabilities import (
    CAPABILITY_CONVERSION_PATTERN,
    FORMS_FAMILY_PLAN,
    INVOKE_CAPABILITIES,
)
from src.domain.forms.actions import (
    FormCapabilityInvokeResult,
    FormPostSubmitAction,
    FormPostSubmitContext,
    FormPostSubmitPlan,
    RecordingCapabilityInvoker,
    action_idempotency_key,
    contact_compatible_plan,
    execute_post_submit_actions,
    validate_post_submit_plan,
)
from src.domain.forms.anti_abuse import (
    ChallengeVerificationResult,
    FormAntiAbusePolicy,
    InMemoryAttemptLimiter,
    StaticChallengeVerifier,
    process_form_submission_request,
)
from src.domain.forms.canonical import (
    FormDefinition,
    FormField,
    FormSubmission,
    contact_block_compatible_definition,
    quote_request_block_compatible_definition,
)
from src.domain.forms.crm import (
    CONTACT_COMPATIBLE_CRM_MAPPING,
    FORMS_CRM_COMPOSITION,
    GENERIC_CRM_ACTION,
    NO_EMAIL_CODE,
    build_crm_contact_upsert_payload,
    crm_contact_upsert_action,
    validate_crm_contact_mapping,
)
from src.domain.forms.files import (
    FormFileRecord,
    InMemoryFormFileDirectory,
    accept_form_submission_with_files,
)
from src.domain.forms.persistence import (
    FormPersistenceError,
    InMemoryFormSubmissionStore,
)
from src.domain.forms.runtime import accept_form_submission

_FIXED_AT = datetime(2026, 9, 13, 21, 0, tzinfo=UTC)
_FSB_A = "fsb_" + ("a" * 32)
_FSB_B = "fsb_" + ("b" * 32)
_FILE_1 = "file_" + ("1" * 32)
_CTX = FormPostSubmitContext(project_id="prj_a")


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

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


def _accept(definition, raw, submission_id=_FSB_A):
    result = accept_form_submission(
        definition,
        raw,
        clock=lambda: _FIXED_AT,
        submission_id_factory=lambda: submission_id,
    )
    assert result.accepted is True
    return result.submission


def _crm_fields(**extra: bool) -> FormDefinition:
    fields = [
        FormField(key="email", type="email", required=False),
        FormField(key="first_name", type="text", required=False),
        FormField(key="last_name", type="text", required=False),
        FormField(key="phone", type="phone", required=False),
        FormField(key="lead_source", type="text", required=False),
        FormField(key="opt_in", type="checkbox", required=False),
        FormField(key="score", type="number", required=False),
        FormField(key="note", type="textarea", required=False),
        FormField(key="pipeline", type="text", required=False),
    ]
    if extra.get("file"):
        fields.append(FormField(key="resume", type="file", required=False))
    return FormDefinition(key="lead", fields=fields)


def test_7i_crm_composition_frozen() -> None:
    assert FORMS_CRM_COMPOSITION["status"] == "frozen"
    assert FORMS_CRM_COMPOSITION["generic_action"] == "crm.contact.upsert"
    assert FORMS_CRM_COMPOSITION["upsert_match_by"] == "email"
    assert FORMS_CRM_COMPOSITION["no_email"] == "crm_identity_missing"
    assert FORMS_CRM_COMPOSITION["no_create_fallback"] is True
    assert FORMS_CRM_COMPOSITION["name_splitting"] is False
    assert FORMS_CRM_COMPOSITION["resolver_in_forms"] is False
    assert FORMS_CRM_COMPOSITION["provider_lead_object"] is False
    assert FORMS_CRM_COMPOSITION["crm_state_on_forms_store"] is False
    assert "crm.contact.create" not in CONTACT_COMPATIBLE_CRM_MAPPING
    assert "form.submit" not in INVOKE_CAPABILITIES
    assert GENERIC_CRM_ACTION in INVOKE_CAPABILITIES
    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["crm_composition_frozen"] is True
    assert FORMS_FAMILY_PLAN["email_composition_frozen"] is True
    assert FORMS_FAMILY_PLAN["slices"][8] == "7i_crm_composition"
    assert FORMS_FAMILY_PLAN["next_slice"] == "9_app_block_modernization"
    assert "crm_status" not in FormSubmission.model_fields


def test_email_only_and_full_canonical_mapping() -> None:
    definition = _crm_fields()
    only = _accept(definition, {"email": "Jane@Example.COM"})
    invoker = RecordingCapabilityInvoker()
    execute_post_submit_actions(
        only,
        FormPostSubmitPlan(actions=(crm_contact_upsert_action(),)),
        context=_CTX,
        invoker=invoker,
        definition=definition,
    )
    assert invoker.calls[0]["payload"] == {"contact": {"email": "Jane@Example.COM"}}
    assert invoker.calls[0]["project_id"] == "prj_a"
    full = _accept(
        definition,
        {
            "email": "Jane@Example.COM",
            "first_name": "Jane",
            "last_name": "Doe",
            "phone": "(555) 123-4567",
        },
    )
    invoker = RecordingCapabilityInvoker()
    plan = FormPostSubmitPlan(
        actions=(
            crm_contact_upsert_action(
                mapping={
                    "email": "email",
                    "first_name": "first_name",
                    "last_name": "last_name",
                    "phone": "phone",
                }
            ),
        )
    )
    execute_post_submit_actions(
        full, plan, context=_CTX, invoker=invoker, definition=definition
    )
    contact = invoker.calls[0]["payload"]["contact"]
    assert contact["email"] == "Jane@Example.COM"
    assert contact["phone"] == "(555) 123-4567"
    assert contact["first_name"] == "Jane"
    assert "submission_id" not in contact
    assert "form_key" not in contact
    assert "project_id" not in invoker.calls[0]["payload"]["contact"]


def test_missing_null_false_zero_in_crm_payload() -> None:
    action = crm_contact_upsert_action(
        mapping={
            "email": "email",
            "phone": "phone",
            "properties.opt_in": "opt_in",
            "properties.score": "score",
            "properties.note": "note",
        }
    )
    built = build_crm_contact_upsert_payload(
        action,
        {
            "email": "a@b.com",
            "opt_in": False,
            "score": 0,
            "note": None,
        },
    )
    contact = built.payload["contact"]
    assert "phone" not in contact
    assert contact["properties"]["opt_in"] is False
    assert contact["properties"]["score"] == 0
    assert contact["properties"]["note"] is None


def test_no_email_skips_crm_and_continues() -> None:
    definition = _crm_fields()
    accepted = _accept(definition, {"first_name": "Jane", "phone": "555"})
    invoker = RecordingCapabilityInvoker()
    plan = FormPostSubmitPlan(
        actions=(
            crm_contact_upsert_action(),
            FormPostSubmitAction(
                key="send_confirmation",
                action_type="email.send",
                mapping={"text": "first_name"},
                constants={"to": "ops@example.com", "subject": "x"},
            ),
        )
    )
    result = execute_post_submit_actions(
        accepted, plan, context=_CTX, invoker=invoker, definition=definition
    )
    assert result.actions[0].status == "skipped"
    assert result.actions[0].error_code == NO_EMAIL_CODE
    assert result.actions[1].status == "succeeded"
    assert [call["capability"] for call in invoker.calls] == ["email.send"]
    assert "crm.contact.create" not in str(invoker.calls)


def test_binding_ambiguous_and_unbound_are_action_failures() -> None:
    definition = contact_block_compatible_definition()
    accepted = _accept(
        definition,
        {"name": "Ada", "email": "ada@example.com", "message": "Hi"},
    )
    invoker = RecordingCapabilityInvoker(
        {
            "crm.contact.upsert": FormCapabilityInvokeResult(
                status="failed",
                error_code="binding_ambiguous",
            )
        }
    )
    result = execute_post_submit_actions(
        accepted,
        contact_compatible_plan(),
        context=_CTX,
        invoker=invoker,
        definition=definition,
        store=InMemoryFormSubmissionStore(),
        persist_first=True,
    )
    by_key = {item.action_key: item for item in result.actions}
    assert by_key["sync_crm"].status == "failed"
    assert by_key["sync_crm"].error_code == "binding_ambiguous"
    assert by_key["send_confirmation"].status == "succeeded"
    assert by_key["notify_automation"].status == "succeeded"
    unbound = RecordingCapabilityInvoker(
        {
            "crm.contact.upsert": FormCapabilityInvokeResult(
                status="failed",
                error_code="crm_unbound",
            )
        }
    )
    again = execute_post_submit_actions(
        accepted,
        contact_compatible_plan(),
        context=_CTX,
        invoker=unbound,
        definition=definition,
    )
    assert again.actions[0].error_code == "crm_unbound"
    assert accepted.submission_id == _FSB_A


def test_hubspot_and_salesforce_see_identical_forms_payload() -> None:
    definition = _crm_fields()
    accepted = _accept(
        definition,
        {"email": "jane@example.com", "first_name": "Jane"},
    )
    plan = FormPostSubmitPlan(
        actions=(
            crm_contact_upsert_action(
                mapping={"email": "email", "first_name": "first_name"}
            ),
        )
    )
    hub = RecordingCapabilityInvoker()
    sales = RecordingCapabilityInvoker()
    execute_post_submit_actions(
        accepted, plan, context=_CTX, invoker=hub, definition=definition
    )
    execute_post_submit_actions(
        accepted, plan, context=_CTX, invoker=sales, definition=definition
    )
    assert hub.calls[0]["payload"] == sales.calls[0]["payload"]
    assert hub.calls[0]["payload"]["contact"]["email"] == "jane@example.com"


def test_rejects_deal_pipeline_file_and_property_collision() -> None:
    definition = _crm_fields(file=True)
    pipeline = crm_contact_upsert_action(
        mapping={"email": "email", "pipeline": "pipeline"}
    )
    assert "unknown_crm_target" in validate_crm_contact_mapping(pipeline, definition)
    collision = crm_contact_upsert_action(
        mapping={"email": "email", "properties.email": "lead_source"}
    )
    assert "canonical_property_collision" in validate_crm_contact_mapping(
        collision, definition
    )
    resume = crm_contact_upsert_action(
        mapping={"email": "email", "properties.resume": "resume"}
    )
    assert "unsupported_crm_file_mapping" in validate_crm_contact_mapping(
        resume, definition
    )
    lead_token = FormPostSubmitAction(
        key="sync_crm",
        action_type="crm.contact.upsert",
        mapping={"email": "email"},
        constants={"lead": True},
    )
    assert "unexpected_crm_constant" in validate_crm_contact_mapping(
        lead_token, definition
    )


def test_provider_specific_lead_does_not_fallback_to_upsert() -> None:
    definition = _crm_fields()
    accepted = _accept(definition, {"email": "jane@example.com"})
    plan = FormPostSubmitPlan(
        actions=(
            FormPostSubmitAction(
                key="sf_lead",
                action_type="salesforce.create_lead",
                mapping={"email": "email"},
                provider_specific=True,
            ),
        )
    )
    invoker = RecordingCapabilityInvoker(
        {
            "salesforce.create_lead": FormCapabilityInvokeResult(
                status="failed",
                error_code="capability_not_invocable",
            )
        }
    )
    result = execute_post_submit_actions(
        accepted, plan, context=_CTX, invoker=invoker, definition=definition
    )
    assert [call["capability"] for call in invoker.calls] == ["salesforce.create_lead"]
    assert result.actions[0].status == "failed"
    assert result.actions[0].error_code == "capability_not_invocable"


def test_two_submissions_same_email_are_independent_actions() -> None:
    definition = _crm_fields()
    raw = {"email": "jane@example.com"}
    a = _accept(definition, raw, _FSB_A)
    b = _accept(definition, raw, _FSB_B)
    plan = FormPostSubmitPlan(actions=(crm_contact_upsert_action(),))
    invoker = RecordingCapabilityInvoker()
    execute_post_submit_actions(
        a, plan, context=_CTX, invoker=invoker, definition=definition
    )
    execute_post_submit_actions(
        b, plan, context=_CTX, invoker=invoker, definition=definition
    )
    keys = [call["idempotency_key"] for call in invoker.calls]
    assert keys == [
        action_idempotency_key(_FSB_A, "sync_crm"),
        action_idempotency_key(_FSB_B, "sync_crm"),
    ]


def test_persist_failure_and_intake_rejection_do_not_invoke_crm() -> None:
    definition = contact_block_compatible_definition()
    accepted = _accept(
        definition,
        {"name": "Ada", "email": "ada@example.com", "message": "Hi"},
    )
    invoker = RecordingCapabilityInvoker()
    result = execute_post_submit_actions(
        accepted,
        contact_compatible_plan(),
        context=_CTX,
        invoker=invoker,
        definition=definition,
        store=BoomStore(),
        persist_first=True,
    )
    assert result.plan_error == "persistence_failed"
    assert invoker.calls == []
    blocked = process_form_submission_request(
        definition,
        {
            "name": "Ada",
            "email": "ada@example.com",
            "message": "Hi",
            "website": "http://bot",
        },
        policy=FormAntiAbusePolicy(),
        project_id="prj_a",
        client_identity="net_a",
        verifier=StaticChallengeVerifier(ChallengeVerificationResult(passed=True)),
        limiter=InMemoryAttemptLimiter(),
        clock=lambda: _FIXED_AT,
        submission_id_factory=lambda: _FSB_A,
    )
    assert blocked.accepted is False
    invalid = accept_form_submission(definition, {"email": "nope"})
    assert invalid.accepted is False


def test_contact_compatible_plan_does_not_map_name_or_message() -> None:
    definition = contact_block_compatible_definition()
    accepted = _accept(
        definition,
        {"name": "Ada Lovelace", "email": "ada@example.com", "message": "Hello"},
    )
    invoker = RecordingCapabilityInvoker()
    execute_post_submit_actions(
        accepted,
        contact_compatible_plan(),
        context=_CTX,
        invoker=invoker,
        definition=definition,
    )
    crm = next(call for call in invoker.calls if call["capability"] == GENERIC_CRM_ACTION)
    assert crm["payload"]["contact"] == {"email": "ada@example.com"}
    assert "first_name" not in crm["payload"]["contact"]
    assert "message" not in crm["payload"]["contact"]
    assert validate_post_submit_plan(contact_compatible_plan(), definition) == ()


def test_file_runtime_cannot_stringify_into_crm() -> None:
    definition = quote_request_block_compatible_definition()
    files = InMemoryFormFileDirectory()
    files.put(FormFileRecord(project_id="prj_a", file_id=_FILE_1))
    runtime = accept_form_submission_with_files(
        definition,
        {
            "name": "Ada",
            "email": "ada@example.com",
            "message": "Quote",
            "attachment": {"file_id": _FILE_1},
        },
        project_id="prj_a",
        files=files,
        clock=lambda: _FIXED_AT,
        submission_id_factory=lambda: _FSB_A,
    )
    action = crm_contact_upsert_action(
        mapping={"email": "email", "properties.resume": "attachment"}
    )
    assert "unsupported_crm_file_mapping" in validate_crm_contact_mapping(
        action, definition
    )
    built = build_crm_contact_upsert_payload(
        action, runtime.submission.submission.values
    )
    assert built.payload is None
    assert built.error_code == "unsupported_crm_file_mapping"


def test_crm_module_does_not_import_providers() -> None:
    source = Path("src/domain/forms/crm.py").read_text(encoding="utf-8")
    lowered = source.lower()
    for token in (
        "hubspot",
        "src.domain.connectors.crm_contact",
        "crm_resolve",
        "portal_id",
        "org_id",
        "soql",
        "sqlalchemy",
        "src.integrations",
    ):
        assert token not in source
        assert token not in lowered
    assert "salesforce" not in lowered
    assert "crm_canonical" in source
