from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import login_required

from app.constants import ROLE_LABELS, ROLES_LOT
from app.helpers import copropriete_required, get_by_public_id, public_id_str
from app.models import Lot, LotMilliemes, LotPersonneRole, LotType, MilliemeType, Personne, db
from app.utils import parse_date

bp = Blueprint("lots", __name__, url_prefix="/lots")


def _lot_types(copropriete):
    return (
        LotType.query.filter_by(copropriete_id=copropriete.id)
        .order_by(LotType.ordre, LotType.libelle)
        .all()
    )


def _lot_type_from_form(copropriete, form) -> LotType | None:
    pid = form.get("lot_type_id", "").strip()
    if not pid:
        return None
    return LotType.query.filter_by(
        public_id=public_id_str(pid), copropriete_id=copropriete.id
    ).first()


def _lot_type_ids_from_form(copropriete, type_public_ids: list[str]) -> list[int]:
    if not type_public_ids:
        return []
    rows = (
        LotType.query.filter(
            LotType.copropriete_id == copropriete.id,
            LotType.public_id.in_([public_id_str(p) for p in type_public_ids]),
        )
        .all()
    )
    return [t.id for t in rows]


def _filtrer_lots(query, q: str, type_ids: list[int]):
    if q:
        like = f"%{q}%"
        query = query.filter(
            db.or_(
                Lot.designation.ilike(like),
                Lot.lot_copro.ilike(like),
                Lot.lot_syndic.ilike(like),
                Lot.numero_fiscal.ilike(like),
            )
        )
    if type_ids:
        query = query.filter(Lot.lot_type_id.in_(type_ids))
    return query


def _remplir_lot_depuis_form(lot: Lot, copropriete, form) -> bool:
    """Remplit le lot depuis le formulaire. Retourne False si validation échoue."""
    lot.designation = form.get("designation", "").strip()
    lot.etage = form.get("etage", "").strip()
    lot.porte = form.get("porte", "").strip()
    lot.lot_syndic = form.get("lot_syndic", "").strip()
    lot.lot_copro = form.get("lot_copro", "").strip()
    lot.numero_fiscal = form.get("numero_fiscal", "").strip()
    lot.surface = float(form["surface"]) if form.get("surface") else None
    lot.cave = form.get("cave", "").strip()
    lot.bal = form.get("bal", "").strip()
    lot.notes = form.get("notes", "").strip()

    type_pid = form.get("lot_type_id", "").strip()
    if type_pid:
        lot_type = _lot_type_from_form(copropriete, form)
        if not lot_type:
            flash("Type de lot invalide.", "danger")
            return False
        lot.lot_type_id = lot_type.id
    else:
        lot.lot_type_id = None

    if not (lot.designation or lot.lot_copro or lot.lot_syndic):
        flash("Indiquez au moins une désignation ou un n° de lot (copro ou syndic).", "danger")
        return False
    return True


@bp.route("/")
@login_required
@copropriete_required
def liste(copropriete):
    q = request.args.get("q", "").strip()
    afficher_supprimes = request.args.get("supprimes") == "1"
    types_filtre = [t for t in request.args.getlist("type") if t]
    type_ids = _lot_type_ids_from_form(copropriete, types_filtre)

    if afficher_supprimes:
        query = Lot.query_archives().filter_by(copropriete_id=copropriete.id)
    else:
        query = Lot.query_actifs().filter_by(copropriete_id=copropriete.id)

    query = _filtrer_lots(query, q, type_ids)
    lots = query.order_by(Lot.designation, Lot.etage, Lot.porte, Lot.lot_copro).all()
    lot_types = _lot_types(copropriete)

    return render_template(
        "lots/liste.html",
        lots=lots,
        copropriete=copropriete,
        afficher_supprimes=afficher_supprimes,
        role_labels=ROLE_LABELS,
        q=q,
        types_filtre=types_filtre,
        lot_types=lot_types,
    )


@bp.route("/nouveau", methods=["GET", "POST"])
@login_required
@copropriete_required
def nouveau(copropriete):
    lot_types = _lot_types(copropriete)
    if request.method == "POST":
        lot = Lot(copropriete_id=copropriete.id)
        if _remplir_lot_depuis_form(lot, copropriete, request.form):
            db.session.add(lot)
            db.session.commit()
            flash("Lot créé.", "success")
            return redirect(url_for("lots.detail", lot_id=lot.public_id))
    return render_template(
        "lots/form.html", lot=None, copropriete=copropriete, lot_types=lot_types
    )


@bp.route("/<uuid:lot_id>")
@login_required
@copropriete_required
def detail(copropriete, lot_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    personnes = (
        Personne.query_actifs()
        .filter_by(copropriete_id=copropriete.id)
        .order_by(Personne.nom)
        .all()
    )
    millieme_types = (
        MilliemeType.query.filter_by(copropriete_id=copropriete.id)
        .order_by(MilliemeType.ordre)
        .all()
    )
    valeurs = {v.type_id: v for v in lot.milliemes_valeurs}
    personnes_choix = [
        {
            "id": p.public_id,
            "label": p.libelle_affiche,
            "q": " ".join(
                part for part in (p.nom, p.prenom, p.raison_sociale) if part
            ),
        }
        for p in personnes
    ]
    return render_template(
        "lots/detail.html",
        lot=lot,
        personnes=personnes,
        personnes_choix=personnes_choix,
        copropriete=copropriete,
        roles_lot=ROLES_LOT,
        role_labels=ROLE_LABELS,
        millieme_types=millieme_types,
        milliemes_valeurs=valeurs,
    )


@bp.route("/<uuid:lot_id>/edit", methods=["GET", "POST"])
@login_required
@copropriete_required
def edit(copropriete, lot_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    lot_types = _lot_types(copropriete)
    if lot.est_supprime:
        flash("Ce lot est supprimé. Restaurez-le pour le modifier.", "warning")
        return redirect(url_for("lots.detail", lot_id=lot.public_id))
    if request.method == "POST":
        if _remplir_lot_depuis_form(lot, copropriete, request.form):
            db.session.commit()
            flash("Lot mis à jour.", "success")
            return redirect(url_for("lots.detail", lot_id=lot.public_id))
    return render_template(
        "lots/form.html", lot=lot, copropriete=copropriete, lot_types=lot_types
    )


@bp.route("/<uuid:lot_id>/milliemes", methods=["POST"])
@login_required
@copropriete_required
def enregistrer_milliemes(copropriete, lot_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    types = MilliemeType.query.filter_by(copropriete_id=copropriete.id).all()
    for mt in types:
        valeur = request.form.get(f"millieme_{mt.id}", "").strip()
        existing = LotMilliemes.query.filter_by(lot_id=lot.id, type_id=mt.id).first()
        if valeur:
            if existing:
                existing.valeur = valeur
            else:
                db.session.add(LotMilliemes(lot_id=lot.id, type_id=mt.id, valeur=valeur))
        elif existing:
            db.session.delete(existing)
    db.session.commit()
    flash("Tantièmes enregistrés.", "success")
    return redirect(url_for("lots.detail", lot_id=lot.public_id))


@bp.route("/<uuid:lot_id>/role", methods=["POST"])
@login_required
@copropriete_required
def ajouter_role(copropriete, lot_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    personne_pid = request.form.get("personne_id", "").strip()
    personne = Personne.query_actifs().filter_by(
        public_id=public_id_str(personne_pid), copropriete_id=copropriete.id
    ).first()
    if not personne:
        flash("Personne invalide.", "danger")
        return redirect(url_for("lots.detail", lot_id=lot.public_id))

    roles_choisis = [r for r in ROLES_LOT if request.form.get(f"role_{r}")]
    if not roles_choisis:
        flash("Sélectionnez au moins un rôle.", "danger")
        return redirect(url_for("lots.detail", lot_id=lot.public_id))

    date_debut = parse_date(request.form.get("date_debut", ""))
    notes = request.form.get("notes", "").strip()
    for role in roles_choisis:
        existe = LotPersonneRole.query_actifs().filter_by(
            lot_id=lot.id,
            personne_id=personne.id,
            role=role,
            date_fin=None,
        ).first()
        if not existe:
            db.session.add(
                LotPersonneRole(
                    lot_id=lot.id,
                    personne_id=personne.id,
                    role=role,
                    date_debut=date_debut,
                    notes=notes,
                )
            )
    db.session.commit()
    flash(f"Rôles enregistrés pour {personne.nom_complet}.", "success")
    return redirect(url_for("lots.detail", lot_id=lot.public_id))


@bp.route("/<uuid:lot_id>/role/<uuid:role_id>/fin", methods=["POST"])
@login_required
@copropriete_required
def fin_role(copropriete, lot_id, role_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    role = (
        LotPersonneRole.query_actifs()
        .filter_by(public_id=public_id_str(role_id), lot_id=lot.id)
        .first_or_404()
    )
    role.date_fin = parse_date(request.form.get("date_fin", ""), default_today=True)
    db.session.commit()
    flash("Fin de rôle enregistrée.", "success")
    return redirect(url_for("lots.detail", lot_id=lot.public_id))


@bp.route("/<uuid:lot_id>/role/<uuid:role_id>/supprimer", methods=["POST"])
@login_required
@copropriete_required
def supprimer_role(copropriete, lot_id, role_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    role = (
        LotPersonneRole.query_actifs()
        .filter_by(public_id=public_id_str(role_id), lot_id=lot.id)
        .first_or_404()
    )
    role.marquer_supprime()
    db.session.commit()
    flash("Rôle supprimé (conservé en base).", "info")
    return redirect(url_for("lots.detail", lot_id=lot.public_id))


@bp.route("/<uuid:lot_id>/supprimer", methods=["POST"])
@login_required
@copropriete_required
def supprimer(copropriete, lot_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    if not lot.est_supprime:
        lot.marquer_supprime()
        db.session.commit()
        flash("Lot supprimé.", "info")
    return redirect(url_for("lots.liste"))


@bp.route("/<uuid:lot_id>/restaurer", methods=["POST"])
@login_required
@copropriete_required
def restaurer(copropriete, lot_id):
    lot = get_by_public_id(Lot, lot_id, copropriete_id=copropriete.id)
    if not lot.est_supprime:
        flash("Ce lot est déjà actif.", "info")
    else:
        lot.restaurer()
        db.session.commit()
        flash("Lot restauré.", "success")
    return redirect(url_for("lots.detail", lot_id=lot.public_id))
