from __future__ import annotations

from datetime import date

from app.excel_import.parsers import normalize_lot_copro
from app.excel_import.reader import ExcelSource, ReleveRow
from app.excel_import.report import ImportReport
from app.models import Compteur, Lot, LotMilliemes, MilliemeType, Releve, ReleveType, db


def _lot_by_syndic(copro_id: int, syndic: str, copro: str) -> Lot | None:
    if syndic:
        lot = (
            Lot.query_actifs()
            .filter_by(copropriete_id=copro_id, lot_syndic=syndic)
            .first()
        )
        if lot:
            return lot
    return (
        Lot.query_actifs()
        .filter_by(copropriete_id=copro_id, lot_copro=copro)
        .first()
    )


def _lot_for_releve(copro_id: int, rel: ReleveRow) -> Lot | None:
    if rel.etage_porte:
        lots = (
            Lot.query_actifs()
            .filter_by(copropriete_id=copro_id, etage=rel.etage_porte)
            .all()
        )
        if len(lots) == 1:
            return lots[0]
    copro = normalize_lot_copro(rel.lot_copro.split("(")[0].strip())
    if copro and "-" not in copro:
        lot = (
            Lot.query_actifs()
            .filter_by(copropriete_id=copro_id, lot_copro=copro)
            .first()
        )
        if lot:
            return lot
    return None


def run(
    source: ExcelSource,
    copro_id: int,
    *,
    dry_run: bool,
) -> ImportReport:
    report = ImportReport("autres", dry_run=dry_run)

    type_mill = (
        MilliemeType.query.filter_by(copropriete_id=copro_id, libelle="Charges communes")
        .first()
    )
    if not type_mill and not dry_run:
        report.add_error("Type de millièmes « Charges communes » introuvable")
        return report

    for lot_draft in source.expand_all_lots():
        lot = None if dry_run else _lot_by_syndic(copro_id, lot_draft.lot_syndic, lot_draft.lot_copro)
        if not dry_run and not lot:
            continue
        if lot_draft.milliemes:
            label = f"Millièmes {lot_draft.milliemes} -> lot {lot_draft.lot_copro}"
            if dry_run:
                report.add_created(label)
            else:
                existing = LotMilliemes.query.filter_by(
                    lot_id=lot.id, type_id=type_mill.id
                ).first()
                if existing:
                    existing.valeur = lot_draft.milliemes
                else:
                    db.session.add(
                        LotMilliemes(
                            lot_id=lot.id,
                            type_id=type_mill.id,
                            valeur=lot_draft.milliemes,
                        )
                    )
                report.add_created(label)

    type_labels = {"eau_froide": "Eau froide", "eau_chaude": "Eau chaude"}
    for rel in source.read_releves_eau():
        if rel.index_valeur is None:
            continue
        lot = None if dry_run else _lot_for_releve(copro_id, rel)
        if not dry_run and not lot:
            report.add_warning(
                f"Compteur {rel.type_eau} : lot « {rel.lot_copro} » introuvable "
                f"({rel.etage_porte})"
            )
            continue

        libelle = type_labels[rel.type_eau]
        rt = None if dry_run else ReleveType.query.filter_by(
            copropriete_id=copro_id, libelle=libelle
        ).first()

        label = (
            f"Compteur {libelle} index {rel.index_valeur:.0f} "
            f"-> lot {rel.lot_copro}"
        )
        if dry_run:
            report.add_created(label)
            continue

        compteur = (
            Compteur.query_actifs()
            .filter_by(lot_id=lot.id, releve_type_id=rt.id if rt else None)
            .first()
        )
        if not compteur:
            compteur = Compteur(
                lot_id=lot.id,
                releve_type_id=rt.id if rt else None,
                type_compteur=rel.type_eau,
                numero="",
            )
            db.session.add(compteur)
            db.session.flush()

        releve = Releve(
            compteur_id=compteur.id,
            date_releve=date.today(),
            index_valeur=rel.index_valeur,
            remarque="Index repris Excel — date de relevé inconnue (archive)",
        )
        db.session.add(releve)
        report.add_created(label)

    if not dry_run:
        db.session.commit()
    return report
