"""Deterministic field lists from a build prompt.

An explicit enumeration ("name, email, phone, subject, and message") is a
confident form-schema delta. Fuzzy wording still goes to the LLM extractor.
The canonical contact fields are not implied once the user listed their own.
"""

from __future__ import annotations

import re

from src.domain.forms.canonical import (
    FORBIDDEN_PROVIDER_TOKENS,
    LOGICAL_KEY_PATTERN,
    RESERVED_CONTEXT_KEYS,
    FormField,
    FormFieldType,
)
from src.domain.projects.form_requirements import FormRequirementDelta

_MAX_FIELDS = 12
_KEY = re.compile(LOGICAL_KEY_PATTERN)

_WITH_FIELDS = re.compile(
    r"\b(?:with|including)\s+(.+?)\s+fields?\b",
    re.IGNORECASE,
)
_FIELDS_LABEL = re.compile(
    r"\bfields?\s*(?:are|:)\s+(.+?)(?:\.|$)",
    re.IGNORECASE,
)
_ADD_FIELDS = re.compile(
    r"\badd\s+(?:an?\s+)?(.+?)\s+fields?\b",
    re.IGNORECASE,
)
_REQUIRED_OPTIONAL = re.compile(
    r"\brequired\s+(.+?)\s+fields?\b(?:\s*,?\s*plus)?\s+optional\s+(.+?)\s+fields?\b",
    re.IGNORECASE,
)

_STOP = frozenset(
    {
        "a",
        "an",
        "and",
        "field",
        "fields",
        "for",
        "form",
        "my",
        "on",
        "optional",
        "our",
        "page",
        "please",
        "plus",
        "required",
        "another",
        "any",
        "custom",
        "extra",
        "few",
        "new",
        "several",
        "show",
        "some",
        "that",
        "the",
        "their",
        "this",
        "to",
        "validate",
        "various",
        "with",
    }
)

_TYPE_BY_KEY: dict[str, FormFieldType] = {
    "email": "email",
    "phone": "phone",
    "telephone": "phone",
    "mobile": "phone",
    "message": "textarea",
    "body": "textarea",
    "comment": "textarea",
    "comments": "textarea",
    "description": "textarea",
}
_OPTIONAL_BY_DEFAULT = frozenset({"phone", "telephone", "mobile", "subject"})
_REQUIRED_BY_DEFAULT = frozenset({"name", "email", "message"})
_ALIASES = {
    "e_mail": "email",
    "full_name": "name",
    "tel": "phone",
}


def enumerated_field_delta(user_prompt: str) -> FormRequirementDelta | None:
    """Return a confident delta when the prompt lists form fields by name."""
    text = " ".join((user_prompt or "").split())
    if not text:
        return None
    form_key = _form_key(text)
    if form_key is None:
        return None

    required_chunk, optional_chunk = _required_optional_chunks(text)
    listed_chunk, replace = _listed_chunk(text)
    if required_chunk or optional_chunk:
        keys = _keys(required_chunk) + _keys(optional_chunk)
        replace = True
    elif listed_chunk:
        keys = _keys(listed_chunk)
    else:
        return None

    ordered = _dedupe(keys)[:_MAX_FIELDS]
    if not ordered:
        return None

    explicit_required = set(_keys(required_chunk))
    explicit_optional = set(_keys(optional_chunk))
    fields = tuple(
        FormField(
            key=key,
            type=_field_type(key),
            required=_required(key, explicit_required, explicit_optional),
            label=key.replace("_", " ").title(),
        )
        for key in ordered
    )
    return FormRequirementDelta(
        form_key=form_key,
        add_fields=fields,
        replace_fields=replace,
    )


def _form_key(text: str) -> str | None:
    lowered = text.lower()
    if re.search(r"\bquote(?:\s+request)?\b", lowered):
        return "quote_request"
    if re.search(r"\bcontact\b", lowered):
        return "contact"
    if re.search(r"\bform\b", lowered):
        return "contact"
    return None


def _required_optional_chunks(text: str) -> tuple[str, str]:
    match = _REQUIRED_OPTIONAL.search(text)
    if not match:
        return "", ""
    return match.group(1), match.group(2)


def _listed_chunk(text: str) -> tuple[str, bool]:
    """Return (chunk, replace_existing_fields)."""
    labeled = _FIELDS_LABEL.search(text)
    if labeled:
        return labeled.group(1), True
    listed = _WITH_FIELDS.search(text)
    if listed:
        return listed.group(1), True
    added = _ADD_FIELDS.search(text)
    if added:
        return added.group(1), False
    return "", False


def _keys(chunk: str) -> list[str]:
    if not chunk:
        return []
    normalized = chunk.replace("e-mail", "email").replace("e mail", "email")
    keys: list[str] = []
    for part in re.split(r",|\band\b", normalized, flags=re.IGNORECASE):
        words = [
            word
            for word in re.findall(r"[a-z0-9]+", part.lower())
            if word not in _STOP
        ]
        if not words:
            continue
        key = _ALIASES.get("_".join(words), "_".join(words))
        if not _KEY.fullmatch(key):
            continue
        if key in RESERVED_CONTEXT_KEYS:
            continue
        if any(token.lower() == key for token in FORBIDDEN_PROVIDER_TOKENS):
            continue
        keys.append(key)
    return keys


def _dedupe(keys: list[str]) -> list[str]:
    seen: set[str] = set()
    ordered: list[str] = []
    for key in keys:
        if key in seen:
            continue
        seen.add(key)
        ordered.append(key)
    return ordered


def _field_type(key: str) -> FormFieldType:
    return _TYPE_BY_KEY.get(key, "text")


def _required(key: str, explicit_required: set[str], explicit_optional: set[str]) -> bool:
    if key in explicit_optional:
        return False
    if key in explicit_required:
        return True
    if key in _OPTIONAL_BY_DEFAULT:
        return False
    if key in _REQUIRED_BY_DEFAULT:
        return True
    return False
