"""Forms → canonical email.send. Not an Email Template domain.

Maps accepted form values and action literals onto `email.send`.
Sender, provider selection, and delivery stay in the email capability runtime.
"""
from __future__ import annotations

from collections.abc import Mapping
from typing import Any

from pydantic import BaseModel, ConfigDict, StrictBool, StrictStr, ValidationError

from src.domain.connectors.schemas import InvokeEmailSendRequest
from src.domain.forms.actions import FormPostSubmitAction
from src.domain.forms.canonical import (
    STRING_FIELD_TYPES,
    FormDefinition,
    FormFileRef,
    parse_form_file_ref,
)

GENERIC_EMAIL_ACTION = "email.send"
NO_RECIPIENT_STATUS = "skipped"
NO_RECIPIENT_CODE = "email_recipient_missing"
INTERPOLATION = False

EMAIL_SEND_TARGETS: frozenset[str] = frozenset(
    {"to", "subject", "text", "html", "reply_to"}
)
REQUIRED_EMAIL_TARGETS: frozenset[str] = frozenset({"to", "subject", "text"})
FORBIDDEN_EMAIL_CONSTANT_KEYS: frozenset[str] = frozenset(
    {
        "from",
        "from_address",
        "template_id",
        "template_key",
        "cc",
        "bcc",
        "internal",
        "delivery_scope",
    }
)

CONTACT_CONFIRMATION_SUBJECT = "Contact received"
CONTACT_CONFIRMATION_MAPPING: dict[str, str] = {"to": "email", "text": "message"}

FORMS_EMAIL_COMPOSITION: dict[str, Any] = {
    "slice_id": "7j_email_composition",
    "status": "frozen",
    "generic_action": GENERIC_EMAIL_ACTION,
    "email_templates": False,
    "interpolation": INTERPOLATION,
    "no_recipient": NO_RECIPIENT_CODE,
    "no_recipient_status": NO_RECIPIENT_STATUS,
    "attachments": False,
    "sender_in_forms": False,
    "resolver_in_forms": False,
    "legacy_contact_formatter": "compatibility_helper_not_generic_renderer",
    "template_consumption": "additive",
    "public_bff": False,
    "form_submit_capability": False,
    "codegen": False,
    "app_block_migrated": False,
    "docs": "docs/audits/FORMS_EMAIL_COMPOSITION_7J.md",
}


class EmailSendBuild(BaseModel):
    model_config = ConfigDict(extra="forbid", frozen=True)
    skipped: StrictBool
    payload: dict[str, Any] | None = None
    error_code: StrictStr | None = None


def email_send_action(
    *,
    key: str = "send_confirmation",
    mapping: Mapping[str, str] | None = None,
    constants: Mapping[str, str] | None = None,
    optional: bool = False,
) -> FormPostSubmitAction:
    return FormPostSubmitAction(
        key=key,
        action_type=GENERIC_EMAIL_ACTION,
        mapping=dict(mapping or CONTACT_CONFIRMATION_MAPPING),
        constants=dict(constants or {"subject": CONTACT_CONFIRMATION_SUBJECT}),
        optional=optional,
    )


def _source_field(definition: FormDefinition, key: str):
    for field in definition.fields:
        if field.key == key:
            return field
    return None


def validate_email_mapping(
    action: FormPostSubmitAction,
    definition: FormDefinition,
) -> tuple[str, ...]:
    if action.action_type != GENERIC_EMAIL_ACTION or action.provider_specific:
        return ()
    if action.template is not None:
        from src.domain.forms.email_template import validate_email_template_mapping

        return validate_email_template_mapping(action, definition)
    errors: list[str] = []
    configured: set[str] = set()
    for target, source in action.mapping.items():
        if target not in EMAIL_SEND_TARGETS:
            errors.append("unknown_email_target")
            continue
        configured.add(target)
        field = _source_field(definition, source)
        if field is None:
            errors.append("unknown_source_field")
            continue
        if field.type == "file":
            errors.append("unsupported_email_file_mapping")
            continue
        if field.type not in STRING_FIELD_TYPES:
            errors.append("unsupported_email_source_type")
    for key, value in action.constants.items():
        if key in FORBIDDEN_EMAIL_CONSTANT_KEYS or key not in EMAIL_SEND_TARGETS:
            errors.append("unexpected_email_constant")
            continue
        if not isinstance(value, str):
            errors.append("unsupported_email_literal_type")
            continue
        configured.add(key)
    missing = REQUIRED_EMAIL_TARGETS - configured
    if missing:
        errors.append("missing_email_content_config")
    return tuple(dict.fromkeys(errors))


def _is_file_value(value: Any) -> bool:
    if isinstance(value, FormFileRef):
        return True
    return parse_form_file_ref(value) is not None


def _usable_recipient(value: Any) -> bool:
    return isinstance(value, str) and bool(value.strip())


def build_email_send_payload(
    action: FormPostSubmitAction,
    values: Mapping[str, Any],
    *,
    renderer: Any = None,
    project_id: str | None = None,
) -> EmailSendBuild:
    """Concrete email.send fields only. Literals are not interpolated."""
    if action.template is not None:
        from src.domain.forms.email_template import build_email_send_payload_from_template

        return build_email_send_payload_from_template(
            action,
            values,
            renderer=renderer,
            project_id=project_id,
        )
    payload: dict[str, Any] = {}
    mapped_to = "to" in action.mapping
    for target, source in action.mapping.items():
        if target not in EMAIL_SEND_TARGETS or source not in values:
            continue
        value = values[source]
        if _is_file_value(value):
            return EmailSendBuild(
                skipped=False,
                error_code="unsupported_email_file_mapping",
            )
        if value is None or not isinstance(value, str):
            if target == "to":
                return EmailSendBuild(
                    skipped=True,
                    error_code=NO_RECIPIENT_CODE,
                )
            return EmailSendBuild(
                skipped=False,
                error_code="unsupported_email_source_type",
            )
        payload[target] = value
    for key, value in action.constants.items():
        if key in EMAIL_SEND_TARGETS and isinstance(value, str):
            payload[key] = value
    if mapped_to and not _usable_recipient(payload.get("to")):
        return EmailSendBuild(skipped=True, error_code=NO_RECIPIENT_CODE)
    if not _usable_recipient(payload.get("to")):
        return EmailSendBuild(skipped=True, error_code=NO_RECIPIENT_CODE)
    subject = payload.get("subject")
    text = payload.get("text")
    if not isinstance(subject, str) or not subject.strip():
        return EmailSendBuild(skipped=False, error_code="email_content_missing")
    if not isinstance(text, str) or not text.strip():
        return EmailSendBuild(skipped=False, error_code="email_content_missing")
    try:
        InvokeEmailSendRequest.model_validate(payload)
    except ValidationError:
        return EmailSendBuild(skipped=False, error_code="email_payload_invalid")
    return EmailSendBuild(skipped=False, payload=payload)


def contact_legacy_notification_payload(
    values: Mapping[str, Any],
    *,
    notify_to: str,
) -> dict[str, Any]:
    """Compatibility with generated-app owner notify. Not a template engine."""
    recipients = [part.strip() for part in notify_to.replace(",", " ").split() if part.strip()]
    name = values.get("name") if isinstance(values.get("name"), str) else ""
    email = values.get("email") if isinstance(values.get("email"), str) else ""
    phone = values.get("phone") if isinstance(values.get("phone"), str) else ""
    message = values.get("message") if isinstance(values.get("message"), str) else ""
    lines = [f"Name: {name}", f"Email: {email}"]
    if phone:
        lines.append(f"Phone: {phone}")
    lines.extend(["", message])
    payload: dict[str, Any] = {
        "to": recipients,
        "subject": f"New contact form submission from {name or 'a visitor'}",
        "text": "\n".join(lines),
    }
    if email:
        payload["reply_to"] = email
    return payload
