from __future__ import annotations

import re
from dataclasses import dataclass, field


def _clean(value) -> str:
    if value is None:
        return ""
    return str(value).replace("\xa0", " ").strip()


def split_multi_values(text: str) -> list[str]:
    """Découpe tél/mails sur retours ligne, tirets entre numéros, point-virgule."""
    raw = _clean(text)
    if not raw:
        return []
    parts = re.split(r"[\n;]+", raw)
    out: list[str] = []
    for part in parts:
        part = part.strip()
        if not part:
            continue
        # "06.12.34.56.78 - 06.98.76.54.32"
        sub = re.split(r"\s+-\s+", part)
        for s in sub:
            s = s.strip(" -")
            if s:
                out.append(s)
    return out


def split_nom_parts(nom: str) -> list[str]:
    nom = _clean(nom)
    if not nom:
        return []
    parts = re.split(r"\s*[/&]\s*|\s+-\s+|\n", nom)
    return [p.strip() for p in parts if p.strip()]


def split_prenom_parts(prenom: str) -> list[str]:
    prenom = _clean(prenom)
    if not prenom:
        return []
    parts = re.split(r"\s*&\s*|\s+-\s+|\n|,\s*", prenom)
    return [p.strip() for p in parts if p.strip()]


def looks_like_raison_sociale(text: str) -> bool:
    t = _clean(text).lower()
    if not t:
        return False
    markers = (
        "cabinet",
        "sci",
        "s.c.i",
        "sarl",
        "sas",
        "sa ",
        " gt ",
        "gestion",
        "selarl",
    )
    return any(m in t for m in markers)


def split_range(value: str) -> list[str]:
    """« 1.4 - 1.5 » ou « 6.5  - 6.6 » → deux parties."""
    v = _clean(value)
    if not v:
        return []
    if " - " in v:
        return [p.strip() for p in v.split(" - ", 1)]
    if "-" in v and v.count(".") >= 2:
        return [p.strip() for p in re.split(r"\s*-\s*", v)]
    return [v]


def split_milliemes(value: str) -> list[str]:
    v = _clean(value)
    if not v:
        return []
    if "+" in v:
        return [p.strip() for p in re.split(r"\s*\+\s*", v) if p.strip()]
    return [v]


def normalize_lot_copro(value: str) -> str:
    v = _clean(value)
    if re.fullmatch(r"0?\d+", v):
        return str(int(v))
    return v


def split_lot_copro_parts(value: str) -> list[str]:
    v = _clean(value)
    if not v:
        return []
    if "-" in v and not v.startswith("P"):
        parts = [p.strip() for p in v.split("-")]
        return [normalize_lot_copro(p) for p in parts if p]
    return [normalize_lot_copro(v)]


def parse_adresse_ligne(adresse: str) -> tuple[str, str, str]:
    """Découpe grossière : ligne1, CP, ville."""
    a = _clean(adresse)
    if not a:
        return "", "", ""
    m = re.search(r"(\d{5})\s+(.+)$", a)
    if m:
        cp = m.group(1)
        ville = m.group(2).strip()
        ligne1 = a[: m.start()].strip(" ,")
        return ligne1, cp, ville
    return a, "", ""


@dataclass
class ContactDraft:
    libelle: str = ""
    valeur: str = ""


@dataclass
class PersonDraft:
    """Personne extraite avant insertion."""

    key: str
    civilite: str = ""
    nom: str = ""
    prenom: str = ""
    raison_sociale: str = ""
    adresse: str = ""
    telephones: list[str] = field(default_factory=list)
    emails: list[str] = field(default_factory=list)
    autres_coordonnees: str = ""
    notes: str = ""
    source: str = ""

    @property
    def label(self) -> str:
        if self.raison_sociale:
            contact = " ".join(p for p in (self.prenom, self.nom) if p)
            return f"{self.raison_sociale} ({contact})" if contact else self.raison_sociale
        return " ".join(p for p in (self.prenom, self.nom) if p) or self.nom


def person_key(
    *,
    nom: str = "",
    prenom: str = "",
    raison_sociale: str = "",
) -> str:
    parts = [
        _clean(raison_sociale).upper(),
        _clean(nom).upper(),
        _clean(prenom).upper(),
    ]
    return "|".join(parts)


def expand_proprietaires(nom: str, prenom: str) -> list[PersonDraft]:
    if looks_like_raison_sociale(nom) or looks_like_raison_sociale(prenom):
        full = " ".join(p for p in (nom, prenom) if p).strip()
        return [
            PersonDraft(
                key=person_key(raison_sociale=full),
                raison_sociale=full,
                source="proprietaire",
            )
        ]

    noms = split_nom_parts(nom) or [""]
    prenoms = split_prenom_parts(prenom)

    if len(noms) == 1 and len(prenoms) <= 1:
        return [
            PersonDraft(
                key=person_key(nom=noms[0], prenom=prenom),
                nom=noms[0],
                prenom=prenom,
                source="proprietaire",
            )
        ]

    drafts: list[PersonDraft] = []
    if len(noms) == len(prenoms) and len(noms) > 1:
        for n, p in zip(noms, prenoms):
            drafts.append(
                PersonDraft(
                    key=person_key(nom=n, prenom=p),
                    nom=n,
                    prenom=p,
                    source="proprietaire",
                )
            )
        return drafts

    if len(noms) > 1 and len(prenoms) == 1:
        for n in noms:
            drafts.append(
                PersonDraft(
                    key=person_key(nom=n, prenom=prenoms[0]),
                    nom=n,
                    prenom=prenoms[0],
                    source="proprietaire",
                )
            )
        return drafts

    if len(noms) == 1 and len(prenoms) > 1:
        for p in prenoms:
            drafts.append(
                PersonDraft(
                    key=person_key(nom=noms[0], prenom=p),
                    nom=noms[0],
                    prenom=p,
                    source="proprietaire",
                )
            )
        return drafts

    for n in noms:
        drafts.append(
            PersonDraft(
                key=person_key(nom=n, prenom=prenom),
                nom=n,
                prenom=prenom,
                source="proprietaire",
            )
        )
    return drafts


def expand_occupants(
    civilite: str,
    nom: str,
    prenom: str,
) -> list[PersonDraft]:
    if not _clean(nom) and not _clean(prenom):
        return []

    civ_lines = [c.strip() for c in _clean(civilite).split("\n") if c.strip()]
    nom_lines = [n.strip() for n in _clean(nom).split("\n") if n.strip()]
    prenom_lines = [p.strip() for p in _clean(prenom).split("\n") if p.strip()]

    if len(nom_lines) > 1 or len(prenom_lines) > 1:
        count = max(len(nom_lines), len(prenom_lines), 1)
        drafts = []
        for i in range(count):
            n = nom_lines[i] if i < len(nom_lines) else (nom_lines[0] if nom_lines else "")
            p = prenom_lines[i] if i < len(prenom_lines) else (
                prenom_lines[0] if prenom_lines else ""
            )
            civ = civ_lines[i] if i < len(civ_lines) else (civ_lines[0] if civ_lines else "")
            drafts.append(
                PersonDraft(
                    key=person_key(nom=n, prenom=p),
                    civilite=civ,
                    nom=n,
                    prenom=p,
                    source="occupant",
                )
            )
        return drafts

    return [
        PersonDraft(
            key=person_key(nom=nom, prenom=prenom),
            civilite=civilite,
            nom=nom,
            prenom=prenom,
            source="occupant",
        )
    ]


def expand_gestionnaire(text: str) -> PersonDraft | None:
    t = _clean(text)
    if not t:
        return None
    return PersonDraft(
        key=person_key(raison_sociale=t),
        raison_sociale=t,
        source="gestionnaire",
    )
