from functools import wraps
from uuid import UUID

from flask import abort, flash, redirect, session, url_for
from flask_login import current_user

from app.models import Copropriete, UserCopropriete, db


def public_id_str(value) -> str:
    """Normalise un identifiant public (str ou UUID Flask)."""
    return str(value)


def get_by_public_id(model, public_id, **filters):
    return model.query.filter_by(
        public_id=public_id_str(public_id), **filters
    ).first_or_404()


def get_copropriete_by_public_id(public_id) -> Copropriete | None:
    return Copropriete.query_actifs().filter_by(
        public_id=public_id_str(public_id)
    ).first()


def resolve_copropriete_public_id(public_id) -> Copropriete | None:
    copro = get_copropriete_by_public_id(public_id)
    if not copro or not user_can_access_copropriete(copro.id):
        return None
    return copro


def copropriete_ids_from_public_ids(public_ids: list[str]) -> list[int]:
    if not public_ids:
        return []
    ids = []
    for pid in public_ids:
        pid = pid.strip()
        if not pid:
            continue
        try:
            UUID(pid)
        except ValueError:
            continue
        copro = Copropriete.query_actifs().filter_by(public_id=pid).first()
        if copro:
            ids.append(copro.id)
    return ids

def get_copropriete_id() -> int | None:
    return session.get("copropriete_id")


def set_copropriete_id(copro_id: int) -> None:
    session["copropriete_id"] = copro_id


def user_can_access_copropriete(copro_id: int) -> bool:
    if not current_user.is_authenticated:
        return False
    return copro_id in current_user.accessible_copropriete_ids()


def get_copropriete_active() -> Copropriete | None:
    copro_id = get_copropriete_id()
    if not copro_id or not user_can_access_copropriete(copro_id):
        return None
    copro = Copropriete.query_actifs().filter_by(id=copro_id).first()
    return copro


def copropriete_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        copro = get_copropriete_active()
        if copro is None:
            flash("Sélectionnez une copropriété pour continuer.", "warning")
            return redirect(url_for("main.select_copropriete"))
        return f(*args, copropriete=copro, **kwargs)

    return decorated


def ensure_copropriete_access(copro_id: int) -> Copropriete:
    if not user_can_access_copropriete(copro_id):
        abort(403)
    copro = db.session.get(Copropriete, copro_id)
    if not copro:
        abort(404)
    return copro


def ensure_copropriete_access_by_public_id(public_id) -> Copropriete:
    copro = get_copropriete_by_public_id(public_id)
    if not copro:
        abort(404)
    if not user_can_access_copropriete(copro.id):
        abort(403)
    return copro


def link_user_to_copropriete(user_id: int, copro_id: int) -> None:
    exists = UserCopropriete.query.filter_by(
        user_id=user_id, copropriete_id=copro_id
    ).first()
    if not exists:
        db.session.add(UserCopropriete(user_id=user_id, copropriete_id=copro_id))


def admin_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not current_user.is_authenticated or not current_user.is_admin:
            abort(403)
        return f(*args, **kwargs)

    return decorated


def set_user_coproprietes(user_id: int, copropriete_ids: list[int]) -> None:
    """Remplace les liens utilisateur ↔ copropriétés."""
    UserCopropriete.query.filter_by(user_id=user_id).delete()
    for copro_id in copropriete_ids:
        link_user_to_copropriete(user_id, copro_id)
