"""Zentrales Feld-Mapping zwischen Tabelle A und Vorlage B.

Anpassungen ausschließlich in FIELD_DEFINITIONS vornehmen.
Aliase werden normalisiert (Kleinschreibung, Umlaute, ohne Satzzeichen),
daher reichen sprechende Varianten wie 'Haltervorname' oder 'B_vorname'.
"""

from __future__ import annotations

from dataclasses import dataclass, field

from app.normalize import header_tokens, normalize_header


class FieldKey:
    ADDRESS_NO = "address_no"
    SALUTATION = "salutation"
    TITLE = "title"
    FIRST_NAME = "first_name"
    LAST_NAME = "last_name"
    NAME_AFFIX = "name_affix"
    NAME_AFFIX2 = "name_affix2"
    STREET = "street"
    HOUSE_NO = "house_no"
    ZIP = "zip"
    CITY = "city"
    COUNTRY = "country"
    LETTER_SALUTATION = "letter_salutation"
    EMAIL = "email"
    EMAIL_PRIVATE = "email_private"
    EMAIL_BUSINESS = "email_business"
    BRAND = "brand"
    MODEL_YEAR = "model_year"
    MODEL = "model"
    VARIANT = "variant"
    SERVICE_DATE = "service_date"
    VIN = "vin"
    PLATE = "plate"
    FIRST_REGISTRATION = "first_registration"
    CUSTOMER_NO = "customer_no"
    COMPANY = "company"
    CUSTOMER_NAME = "customer_name"


@dataclass(frozen=True)
class FieldDef:
    key: str
    label: str
    source_aliases: tuple[str, ...]
    target_aliases: tuple[str, ...]
    field_type: str = "text"
    output: bool = True
    source_alias_set: frozenset[str] = field(init=False, repr=False)
    target_alias_set: frozenset[str] = field(init=False, repr=False)

    def __post_init__(self) -> None:
        object.__setattr__(
            self,
            "source_alias_set",
            frozenset(normalize_header(a) for a in self.source_aliases if normalize_header(a)),
        )
        object.__setattr__(
            self,
            "target_alias_set",
            frozenset(normalize_header(a) for a in self.target_aliases if normalize_header(a)),
        )


FIELD_DEFINITIONS: tuple[FieldDef, ...] = (
    FieldDef(
        key=FieldKey.ADDRESS_NO,
        label="Adressnr.",
        source_aliases=(
            "Adressnr.",
            "Adressnr",
            "Adressnummer",
            "Adress-Nr",
            "Adress Nr",
            "Addressnr",
            "Address number",
            "AN",
        ),
        target_aliases=("AN", "AN = Adressnr.", "Adressnr.", "Adressnummer"),
    ),
    FieldDef(
        key=FieldKey.SALUTATION,
        label="Anrede",
        source_aliases=("Anrede", "Anredeform", "Geschlecht", "Salutation"),
        target_aliases=("A", "A = Anrede", "Anrede"),
    ),
    FieldDef(
        key=FieldKey.TITLE,
        label="Titel",
        source_aliases=("Titel", "Akad. Titel", "Akademischer Titel", "Title"),
        target_aliases=("Z1", "Z1 = Titel", "Titel"),
    ),
    FieldDef(
        key=FieldKey.FIRST_NAME,
        label="Vorname",
        source_aliases=(
            "Vorname",
            "Vornamen",
            "Haltervorname",
            "B_vorname",
            "Bvorname",
            "First name",
            "Firstname",
        ),
        target_aliases=("Z2", "Z2 = Vorname", "Vorname"),
    ),
    FieldDef(
        key=FieldKey.LAST_NAME,
        label="Nachname",
        source_aliases=(
            "Nachname",
            "Name",
            "Familienname",
            "Halternachname",
            "B_nachname",
            "Bnachname",
            "Last name",
            "Lastname",
        ),
        target_aliases=("Z3", "Z3 = Nachname", "Nachname"),
    ),
    FieldDef(
        key=FieldKey.NAME_AFFIX,
        label="Namenszusatz",
        source_aliases=("Namenszusatz", "Namenszusatz 1", "Zusatz", "Name2"),
        target_aliases=("Z4", "Z4 = Namenszusatz", "Namenszusatz"),
    ),
    FieldDef(
        key=FieldKey.NAME_AFFIX2,
        label="Namenszusatz2",
        source_aliases=("Namenszusatz2", "Namenszusatz 2", "Zusatz2", "Name3"),
        target_aliases=("Z5", "Z5 = Namenszusatz2", "Namenszusatz2"),
    ),
    FieldDef(
        key=FieldKey.STREET,
        label="Straße",
        source_aliases=(
            "Straße",
            "Strasse",
            "Straße + Hausnummer",
            "Strasse + Hausnummer",
            "Strasse Hausnummer",
            "Anschrift",
            "Adresse",
            "Street",
        ),
        target_aliases=("Z6", "Z6 = Straße + Hausnummer", "Straße + Hausnummer", "Strasse", "Straße"),
        field_type="street",
    ),
    FieldDef(
        key=FieldKey.HOUSE_NO,
        label="Hausnummer",
        source_aliases=("Hausnummer", "Hausnr", "HsNr", "Hsnr", "Nr", "House number"),
        target_aliases=("Hausnummer", "Hausnr"),
        output=False,
    ),
    FieldDef(
        key=FieldKey.ZIP,
        label="PLZ",
        source_aliases=("PLZ", "Postleitzahl", "Postal code", "Zip"),
        target_aliases=("Z7", "Z7 = PLZ", "PLZ", "Postleitzahl"),
        field_type="zip",
    ),
    FieldDef(
        key=FieldKey.CITY,
        label="Ort",
        source_aliases=("Ort", "Stadt", "Wohnort", "City"),
        target_aliases=("Z8", "Z8 = Ort", "Ort", "Stadt"),
    ),
    FieldDef(
        key=FieldKey.COUNTRY,
        label="Land",
        source_aliases=("Land", "Country", "Staat"),
        target_aliases=("Z9", "Z9 = Land", "Land"),
    ),
    FieldDef(
        key=FieldKey.LETTER_SALUTATION,
        label="Briefanrede",
        source_aliases=("Briefanrede", "Anrede Brief", "Letter salutation"),
        target_aliases=("P1", "P1 = Briefanrede", "Briefanrede"),
    ),
    FieldDef(
        key=FieldKey.EMAIL,
        label="E-Mail",
        source_aliases=("E-Mail", "Email", "Mail", "EMail"),
        target_aliases=("E-Mail", "Email"),
        output=False,
    ),
    FieldDef(
        key=FieldKey.EMAIL_PRIVATE,
        label="Kundenmailadresse Privat",
        source_aliases=(
            "E-Mail Privat",
            "Email Privat",
            "Privatmail",
            "Mail privat",
            "KEP",
        ),
        target_aliases=("KEP", "KEP = Kundenmailadresse. Privat", "Kundenmailadresse. Privat"),
    ),
    FieldDef(
        key=FieldKey.EMAIL_BUSINESS,
        label="Kundenmailadresse Business",
        source_aliases=(
            "E-Mail Business",
            "Email Business",
            "E-Mail Dienst",
            "Geschaeftlich",
            "Geschäftlich",
            "KEB",
        ),
        target_aliases=("KEB", "KEB = Kundenmailadresse. Business", "Kundenmailadresse. Business"),
    ),
    FieldDef(
        key=FieldKey.BRAND,
        label="Marke",
        source_aliases=("Marke", "Brand", "Fabrikat", "Marke Volvo"),
        target_aliases=("MV", "MV = Marke Volvo", "Marke Volvo", "Marke"),
    ),
    FieldDef(
        key=FieldKey.MODEL_YEAR,
        label="Modelljahr",
        source_aliases=("Modelljahr", "MJ", "Baujahr", "Model year"),
        target_aliases=("MJ", "MJ = Modelljahr", "Modelljahr"),
    ),
    FieldDef(
        key=FieldKey.MODEL,
        label="Modell",
        source_aliases=("Modell", "Model", "Fahrzeugmodell", "Typ"),
        target_aliases=("M", "M = Modell", "Modell"),
    ),
    FieldDef(
        key=FieldKey.VARIANT,
        label="Variante",
        source_aliases=("Variante", "Ausfuehrung", "Ausführung", "Variant"),
        target_aliases=("V", "V=Variante", "V = Variante", "Variante"),
    ),
    FieldDef(
        key=FieldKey.SERVICE_DATE,
        label="Servicedatum",
        source_aliases=(
            "Servicedatum",
            "letztes Servicedatum",
            "Letzter Service",
            "Service",
            "Inspektion",
        ),
        target_aliases=("S", "S = letztes Servicedatum", "letztes Servicedatum", "Servicedatum"),
        field_type="date",
    ),
    FieldDef(
        key=FieldKey.VIN,
        label="Fahrgestellnr.",
        source_aliases=(
            "Fahrgestellnr.",
            "Fahrgestellnr",
            "Fahrgestellnummer",
            "FIN",
            "VIN",
            "FgNr",
            "FGN",
        ),
        target_aliases=("FGN", "FGN = Fahrgestellnr.", "Fahrgestellnr.", "Fahrgestellnummer", "VIN"),
    ),
    FieldDef(
        key=FieldKey.PLATE,
        label="Kennzeichen",
        source_aliases=(
            "Kennzeichen",
            "amtl. KZ",
            "amtliches Kennzeichen",
            "AKZ",
            "KZ",
            "Nummernschild",
        ),
        target_aliases=("AKZ", "AKZ = amtl. KZ", "amtl. KZ", "Kennzeichen"),
    ),
    FieldDef(
        key=FieldKey.FIRST_REGISTRATION,
        label="Erstzulassung",
        source_aliases=("Erstzulassung", "Erstzulassungsdatum", "EZ", "EZL", "Zulassung"),
        target_aliases=("EZL", "EZL = Erstzulassungsdatum", "Erstzulassungsdatum", "Erstzulassung"),
        field_type="date",
    ),
    FieldDef(
        key=FieldKey.CUSTOMER_NO,
        label="Kundennummer",
        source_aliases=("Kundennummer", "Kdnr", "Kd-Nr", "Customer no", "Kunden-Nr"),
        target_aliases=("Kundennummer", "Kdnr"),
    ),
    FieldDef(
        key=FieldKey.COMPANY,
        label="Firma",
        source_aliases=("Firma", "Firmenname", "Unternehmen", "Company", "Betrieb"),
        target_aliases=("Firma", "Firmenname", "Company"),
    ),
    FieldDef(
        key=FieldKey.CUSTOMER_NAME,
        label="Kundenname",
        source_aliases=("Kundenname", "Kunde", "Name1", "Namenszeile"),
        target_aliases=("Kundenname", "Kunde"),
    ),
)

FIELDS_BY_KEY: dict[str, FieldDef] = {f.key: f for f in FIELD_DEFINITIONS}

CORE_TARGET_FIELDS = (
    FieldKey.STREET,
    FieldKey.ZIP,
    FieldKey.CITY,
    FieldKey.PLATE,
    FieldKey.VIN,
    FieldKey.LETTER_SALUTATION,
)

MIN_SOURCE_MAPPED_FIELDS = 3
MIN_TARGET_MAPPED_FIELDS = 6


def match_columns(headers: list[str], side: str) -> dict[str, int]:
    """Ordnet Spaltenüberschriften Feldschlüsseln zu. Spezifischere Aliase gewinnen."""
    scored: list[tuple[int, str, int]] = []
    for col, header in enumerate(headers):
        tokens = header_tokens(header)
        if not tokens:
            continue
        for field_def in FIELD_DEFINITIONS:
            aliases = field_def.source_alias_set if side == "source" else field_def.target_alias_set
            matched = tokens & aliases
            if not matched:
                continue
            scored.append((max(len(alias) for alias in matched), field_def.key, col))

    scored.sort(key=lambda item: (-item[0], item[2]))
    result: dict[str, int] = {}
    used_cols: set[int] = set()
    for _length, key, col in scored:
        if key in result or col in used_cols:
            continue
        result[key] = col
        used_cols.add(col)
    return result


def field_label(key: str) -> str:
    definition = FIELDS_BY_KEY.get(key)
    return definition.label if definition else key
