from datetime import datetime, timedelta

from fastapi import APIRouter, Depends, Form, Request
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session

from app import models
from app.database import get_db
from app.securite import (
    enregistrer_echec,
    hacher_mot_de_passe,
    obtenir_jeton_csrf,
    reinitialiser_tentatives,
    sauvegarder_sans_bloquer,
    trop_de_tentatives,
    utilisateur_courant,
    verifier_csrf,
    verifier_mot_de_passe,
)

router = APIRouter(tags=["auth"])
templates = Jinja2Templates(directory="app/templates")

DUREE_CONSERVATION_JOURNAL = timedelta(days=180)


def _journaliser_connexion(db: Session, identifiant: str, reussie: bool, request: Request) -> None:
    ip = request.client.host if request.client else None
    db.add(models.JournalConnexion(identifiant=identifiant, reussie=reussie, adresse_ip=ip))
    db.query(models.JournalConnexion).filter(
        models.JournalConnexion.date_heure < datetime.utcnow() - DUREE_CONSERVATION_JOURNAL
    ).delete()
    db.commit()


@router.get("/configuration-initiale")
def formulaire_configuration_initiale(request: Request, db: Session = Depends(get_db)):
    if db.query(models.Utilisateur).count() > 0:
        return RedirectResponse(url="/connexion", status_code=303)
    return templates.TemplateResponse(
        "auth/configuration_initiale.html",
        {"request": request, "csrf_token": obtenir_jeton_csrf(request)},
    )


@router.post("/configuration-initiale")
def creer_premier_admin(
    request: Request,
    csrf_token: str = Form(...),
    identifiant: str = Form(...),
    nom_complet: str = Form(...),
    mot_de_passe: str = Form(...),
    confirmation_mot_de_passe: str = Form(...),
    db: Session = Depends(get_db),
):
    if db.query(models.Utilisateur).count() > 0:
        return RedirectResponse(url="/connexion", status_code=303)

    erreur = None
    if not verifier_csrf(request, csrf_token):
        erreur = "Session expirée, veuillez réessayer."
    elif len(mot_de_passe) < 8:
        erreur = "Le mot de passe doit contenir au moins 8 caractères."
    elif mot_de_passe != confirmation_mot_de_passe:
        erreur = "Les mots de passe ne correspondent pas."

    if erreur:
        return templates.TemplateResponse(
            "auth/configuration_initiale.html",
            {"request": request, "csrf_token": obtenir_jeton_csrf(request), "erreur": erreur},
            status_code=400,
        )

    admin = models.Utilisateur(
        identifiant=identifiant.strip(),
        nom_complet=nom_complet.strip(),
        mot_de_passe_hash=hacher_mot_de_passe(mot_de_passe),
        est_admin=True,
        actif=True,
    )
    db.add(admin)
    db.commit()

    request.session["user_id"] = admin.id
    return RedirectResponse(url="/?succes=Compte administrateur créé", status_code=303)


@router.get("/connexion")
def formulaire_connexion(request: Request, db: Session = Depends(get_db)):
    if db.query(models.Utilisateur).count() == 0:
        return RedirectResponse(url="/configuration-initiale", status_code=303)
    if utilisateur_courant(request, db) is not None:
        return RedirectResponse(url="/", status_code=303)
    return templates.TemplateResponse(
        "auth/connexion.html",
        {"request": request, "csrf_token": obtenir_jeton_csrf(request)},
    )


@router.post("/connexion")
def connexion(
    request: Request,
    csrf_token: str = Form(...),
    identifiant: str = Form(...),
    mot_de_passe: str = Form(...),
    suivant: str = Form("/"),
    db: Session = Depends(get_db),
):
    erreur = None
    if not verifier_csrf(request, csrf_token):
        erreur = "Session expirée, veuillez réessayer."
    elif trop_de_tentatives(identifiant.strip()):
        erreur = "Trop de tentatives échouées. Réessayez dans quelques minutes."
    else:
        utilisateur = (
            db.query(models.Utilisateur)
            .filter(models.Utilisateur.identifiant == identifiant.strip())
            .first()
        )
        if (
            utilisateur is None
            or not utilisateur.actif
            or not verifier_mot_de_passe(mot_de_passe, utilisateur.mot_de_passe_hash)
        ):
            enregistrer_echec(identifiant.strip())
            _journaliser_connexion(db, identifiant.strip(), False, request)
            erreur = "Identifiant ou mot de passe incorrect."
        else:
            reinitialiser_tentatives(identifiant.strip())
            _journaliser_connexion(db, identifiant.strip(), True, request)
            request.session["user_id"] = utilisateur.id
            destination = suivant if suivant.startswith("/") and not suivant.startswith("//") else "/"
            return RedirectResponse(url=destination, status_code=303)

    return templates.TemplateResponse(
        "auth/connexion.html",
        {"request": request, "csrf_token": obtenir_jeton_csrf(request), "erreur": erreur},
        status_code=400,
    )


@router.post("/deconnexion")
def deconnexion(request: Request, csrf_token: str = Form(...)):
    if verifier_csrf(request, csrf_token):
        etait_connecte = bool(request.session.get("user_id"))
        request.session.clear()
        if etait_connecte:
            sauvegarder_sans_bloquer()
    return RedirectResponse(url="/connexion", status_code=303)
