"""Automatische Erzeugung der Briefanrede."""

from __future__ import annotations

import re

from app.mapping import FieldKey
from app.models import CustomerRecord
from app.normalize import clean_text, is_empty, normalize_name

_VALID_PREFIX = re.compile(r"^sehr\s+geehrte", re.IGNORECASE)

_FRAU = {
    "frau",
    "fr",
    "fr.",
    "mrs",
    "ms",
    "mrs.",
    "ms.",
    "weiblich",
    "w",
    "divers frau",
}
_HERR = {
    "herr",
    "hr",
    "hr.",
    "mr",
    "mr.",
    "maennlich",
    "männlich",
    "m",
}
_FIRMA = {
    "firma",
    "fa",
    "fa.",
    "company",
    "unternehmen",
    "betrieb",
    "institution",
    "behoerde",
    "behörde",
}

_COMPANY_HINTS = (
    "gmbh",
    "mbh",
    "ag",
    "kg",
    "ohg",
    "ug",
    "e.k.",
    "e.k",
    "ek",
    "e.v.",
    "ev",
    "ltd",
    "inc",
    "co kg",
    "co. kg",
)


def is_valid_letter_salutation(value: object) -> bool:
    text = clean_text(value)
    if len(text) < 15:
        return False
    return bool(_VALID_PREFIX.search(text))


def _has_doctor_title(title: str) -> bool:
    normalized = normalize_name(title)
    if not normalized:
        return False
    tokens = set(normalized.replace(".", " ").split())
    return "dr" in tokens or "doktor" in tokens or normalized.startswith("dr ")


def _looks_like_company(*parts: str) -> bool:
    blob = normalize_name(" ".join(p for p in parts if p))
    if not blob:
        return False
    return any(hint.replace(".", "") in blob for hint in _COMPANY_HINTS)


def infer_addressee_kind(record: CustomerRecord) -> str:
    raw = clean_text(record.get(FieldKey.SALUTATION)).lower().rstrip(".")
    compact = raw.replace(" ", "")
    if raw in _FIRMA or compact in {"firma", "fa"}:
        return "firma"
    if raw in _FRAU or compact in {"frau", "fr"}:
        return "frau"
    if raw in _HERR or compact in {"herr", "hr"}:
        return "herr"

    company = clean_text(record.get(FieldKey.COMPANY))
    last = clean_text(record.get(FieldKey.LAST_NAME))
    first = clean_text(record.get(FieldKey.FIRST_NAME))
    customer_name = clean_text(record.get(FieldKey.CUSTOMER_NAME))
    if company or _looks_like_company(last, customer_name, first):
        return "firma"
    return "unknown"


def generate_letter_salutation(record: CustomerRecord) -> str:
    existing = record.get(FieldKey.LETTER_SALUTATION)
    if is_valid_letter_salutation(existing):
        return clean_text(existing)

    kind = infer_addressee_kind(record)
    last = clean_text(record.get(FieldKey.LAST_NAME))
    title = clean_text(record.get(FieldKey.TITLE))
    doctor = _has_doctor_title(title)
    if last.lower().startswith("dr.") or last.lower().startswith("dr "):
        doctor = True
        last = re.sub(r"^(dr\.?\s+)", "", last, flags=re.IGNORECASE).strip()

    if kind == "firma" or not last or kind == "unknown":
        if kind == "frau" and last:
            if doctor:
                return f"Sehr geehrte Frau Dr. {last}"
            return f"Sehr geehrte Frau {last}"
        if kind == "herr" and last:
            if doctor:
                return f"Sehr geehrter Herr Dr. {last}"
            return f"Sehr geehrter Herr {last}"
        return "Sehr geehrte Damen und Herren"

    if kind == "frau":
        if doctor:
            return f"Sehr geehrte Frau Dr. {last}"
        return f"Sehr geehrte Frau {last}"
    if kind == "herr":
        if doctor:
            return f"Sehr geehrter Herr Dr. {last}"
        return f"Sehr geehrter Herr {last}"
    return "Sehr geehrte Damen und Herren"


def apply_salutations(records: list[CustomerRecord]) -> int:
    generated = 0
    for record in records:
        current = record.get(FieldKey.LETTER_SALUTATION)
        if is_valid_letter_salutation(current):
            continue
        record.set(FieldKey.LETTER_SALUTATION, generate_letter_salutation(record))
        if is_empty(current):
            generated += 1
    return generated
