from __future__ import annotations

from app.excel_import.parsers import (
    PersonDraft,
    expand_gestionnaire,
    expand_occupants,
    expand_proprietaires,
    person_key,
)
from app.excel_import.reader import ExcelSource, expand_info_row
from app.excel_import.report import ImportReport
from app.models import Lot, LotPersonneRole, Personne, db


def _find_person(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()
        )
    return (
        Personne.query_actifs()
        .filter_by(copropriete_id=copro_id, nom=draft.nom, prenom=draft.prenom)
        .first()
    )


def _find_lot(copro_id: int, import_key: str, lot_syndic: str, lot_copro: str) -> Lot | None:
    lots = (
        Lot.query_actifs()
        .filter_by(copropriete_id=copro_id, lot_syndic=lot_syndic, lot_copro=lot_copro)
        .all()
    )
    if len(lots) == 1:
        return lots[0]
    if not lots:
        return (
            Lot.query_actifs()
            .filter_by(copropriete_id=copro_id, lot_copro=lot_copro)
            .first()
        )
    return lots[0]


def _add_role(
    report: ImportReport,
    lot: Lot,
    personne: Personne,
    role: str,
    *,
    dry_run: bool,
) -> None:
    label = f"{role} : {personne.libelle_affiche} -> lot {lot.lot_copro}"
    if dry_run:
        report.add_created(label)
        return
    exists = (
        LotPersonneRole.query_actifs()
        .filter_by(lot_id=lot.id, personne_id=personne.id, role=role, date_fin=None)
        .first()
    )
    if exists:
        report.add_skipped(label)
        return
    db.session.add(LotPersonneRole(lot_id=lot.id, personne_id=personne.id, role=role))
    report.add_created(label)


def run(
    source: ExcelSource,
    copro_id: int,
    *,
    dry_run: bool,
) -> ImportReport:
    report = ImportReport("roles", dry_run=dry_run)

    for info_row in source.read_info_rows():
        for lot_draft in expand_info_row(info_row):
            lot = None if dry_run else _find_lot(
                copro_id, lot_draft.import_key, lot_draft.lot_syndic, lot_draft.lot_copro
            )
            if not dry_run and not lot:
                report.add_error(
                    f"Lot introuvable pour rôles : {lot_draft.import_key} "
                    f"(ligne {info_row.row_num})"
                )
                continue

            for p_draft in expand_proprietaires(info_row.nom_copro, info_row.prenom_copro):
                personne = None if dry_run else _find_person(copro_id, p_draft)
                if not dry_run and not personne:
                    report.add_error(f"Propriétaire introuvable : {p_draft.label}")
                    continue
                if dry_run:
                    report.add_created(
                        f"proprietaire : {p_draft.label} -> lot {lot_draft.lot_copro}"
                    )
                else:
                    _add_role(report, lot, personne, "proprietaire", dry_run=False)

            if info_row.nom_occ or info_row.prenom_occ:
                occ_drafts = expand_occupants(
                    info_row.civilite_occ, info_row.nom_occ, info_row.prenom_occ
                )
                for o_draft in occ_drafts:
                    personne = None if dry_run else _find_person(copro_id, o_draft)
                    if not dry_run and not personne:
                        report.add_error(f"Occupant introuvable : {o_draft.label}")
                        continue
                    if dry_run:
                        report.add_created(
                            f"occupant : {o_draft.label} -> lot {lot_draft.lot_copro}"
                        )
                    else:
                        _add_role(report, lot, personne, "occupant", dry_run=False)

            g_draft = expand_gestionnaire(info_row.gestionnaire)
            if g_draft:
                personne = None if dry_run else _find_person(copro_id, g_draft)
                if not dry_run and not personne:
                    report.add_error(f"Gestionnaire introuvable : {g_draft.label}")
                elif dry_run:
                    report.add_created(
                        f"gestionnaire : {g_draft.label} -> lot {lot_draft.lot_copro}"
                    )
                elif personne:
                    _add_role(report, lot, personne, "gestionnaire", dry_run=False)

    if not dry_run:
        db.session.commit()
    return report
