from __future__ import annotations

from app.excel_import.parsers import (
    PersonDraft,
    expand_gestionnaire,
    expand_occupants,
    expand_proprietaires,
    parse_adresse_ligne,
    person_key,
    split_multi_values,
)
from app.excel_import.reader import ExcelSource, InfoRow
from app.excel_import.report import ImportReport
from app.models import Adresse, Email, Personne, Telephone, db
from app.phone import normalize_phone


def _collect_persons_from_row(row: InfoRow) -> list[PersonDraft]:
    drafts: list[PersonDraft] = []

    for p in expand_proprietaires(row.nom_copro, row.prenom_copro):
        p.adresse = row.adresse
        p.telephones = split_multi_values(row.tel_copro)
        p.emails = split_multi_values(row.mail_copro)
        p.autres_coordonnees = row.autres_coordonnees
        drafts.append(p)

    if row.nom_occ or row.prenom_occ:
        for o in expand_occupants(row.civilite_occ, row.nom_occ, row.prenom_occ):
            o.telephones = split_multi_values(row.tel_occ)
            o.emails = split_multi_values(row.mail_occ)
            drafts.append(o)

    g = expand_gestionnaire(row.gestionnaire)
    if g:
        drafts.append(g)

    return drafts


def _merge_draft(target: PersonDraft, incoming: PersonDraft) -> None:
    if incoming.civilite and not target.civilite:
        target.civilite = incoming.civilite
    if incoming.adresse and not target.adresse:
        target.adresse = incoming.adresse
    for t in incoming.telephones:
        if t not in target.telephones:
            target.telephones.append(t)
    for e in incoming.emails:
        if e.lower() not in {x.lower() for x in target.emails}:
            target.emails.append(e)
    if incoming.autres_coordonnees:
        target.autres_coordonnees = (
            target.autres_coordonnees + "\n" + incoming.autres_coordonnees
        ).strip()
    if incoming.notes:
        target.notes = (target.notes + "\n" + incoming.notes).strip()


def _find_existing(copro_id: int, draft: PersonDraft) -> Personne | None:
    if draft.raison_sociale:
        return (
            Personne.query_actifs()
            .filter_by(copropriete_id=copro_id, raison_sociale=draft.raison_sociale)
            .first()
        )
    q = Personne.query_actifs().filter_by(
        copropriete_id=copro_id, nom=draft.nom, prenom=draft.prenom
    )
    return q.first()


def _apply_contacts(personne: Personne, draft: PersonDraft) -> None:
    for i, numero in enumerate(draft.telephones):
        n = normalize_phone(numero)
        if not n:
            continue
        if any(t.numero == n for t in personne.telephones):
            continue
        db.session.add(
            Telephone(personne=personne, numero=n, libelle="Import" if i else "")
        )
    for i, addr in enumerate(draft.emails):
        a = addr.strip()
        if not a:
            continue
        if any(e.adresse.lower() == a.lower() for e in personne.emails):
            continue
        db.session.add(Email(personne=personne, adresse=a, libelle="Import" if i else ""))
    if draft.adresse:
        ligne1, cp, ville = parse_adresse_ligne(draft.adresse)
        if ligne1 and not personne.adresses:
            db.session.add(
                Adresse(
                    personne=personne,
                    libelle="Courrier",
                    ligne1=ligne1,
                    code_postal=cp,
                    ville=ville,
                )
            )
    notes_parts = [p for p in (draft.autres_coordonnees, draft.notes) if p]
    if notes_parts:
        extra = "\n".join(notes_parts)
        personne.notes = (personne.notes + "\n" + extra).strip() if personne.notes else extra


def run(
    source: ExcelSource,
    copro_id: int,
    *,
    dry_run: bool,
) -> ImportReport:
    report = ImportReport("personnes", dry_run=dry_run)
    registry: dict[str, PersonDraft] = {}

    for row in source.read_info_rows():
        for draft in _collect_persons_from_row(row):
            if draft.key in registry:
                _merge_draft(registry[draft.key], draft)
            else:
                registry[draft.key] = draft

    for key, draft in sorted(registry.items(), key=lambda x: x[1].label):
        existing = None if dry_run else _find_existing(copro_id, draft)
        if existing:
            if not dry_run:
                _apply_contacts(existing, draft)
            report.add_warning(f"Déjà présent, contacts fusionnés : {draft.label}")
            continue

        if dry_run:
            report.add_created(draft.label)
            continue

        personne = Personne(
            copropriete_id=copro_id,
            civilite=draft.civilite,
            nom=draft.nom or (draft.raison_sociale[:120] if draft.raison_sociale else "—"),
            prenom=draft.prenom,
            raison_sociale=draft.raison_sociale,
        )
        db.session.add(personne)
        db.session.flush()
        _apply_contacts(personne, draft)
        report.add_created(draft.label)

    if not dry_run:
        db.session.commit()
    return report
