from datetime import date

from fastapi import APIRouter, Depends, Form, HTTPException, Request, UploadFile
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.rapports import (
    GROUPEMENTS_PERIODE,
    THEMES_RAPPORT,
    THEMES_RAPPORT_FERME,
    achats_par_fournisseur,
    achats_par_fournisseur_periode,
    achats_periode,
    autres_frais_par_type,
    autres_frais_par_type_periode,
    autres_frais_periode,
    frais_personnel_par_employe,
    frais_personnel_par_employe_periode,
    frais_personnel_par_periode,
    frais_personnel_periode,
    grand_livre_fournisseur,
    lister_employes,
    lister_mois_comptable,
    lister_types_frais,
    resume_periode_comptable,
)
from app.securite import verifier_jeton_csrf
from app.uploads import enregistrer_fichier, supprimer_fichier
from app.utils import vers_float, vers_int

router = APIRouter(prefix="/comptable", tags=["comptable"])
templates = Jinja2Templates(directory="app/templates")


@router.get("")
def accueil_comptable(
    request: Request,
    db: Session = Depends(get_db),
    mois: str | None = None,
    fournisseur_id: str | None = None,
    type_frais: str | None = None,
    employe: str | None = None,
    groupement: str = "mois",
):
    if groupement not in GROUPEMENTS_PERIODE:
        groupement = "mois"
    fournisseur_id_int = vers_int(fournisseur_id)
    fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
    achats = db.query(models.Achat).order_by(models.Achat.date_achat.desc()).all()
    reglements = db.query(models.Reglement).order_by(models.Reglement.date_reglement.desc()).all()
    frais_personnel = db.query(models.FraisPersonnel).order_by(models.FraisPersonnel.id.desc()).all()
    autres_frais = db.query(models.AutreFrais).order_by(models.AutreFrais.date_frais.desc()).all()
    return templates.TemplateResponse(
        "comptable/accueil.html",
        {
            "request": request,
            "fournisseurs": fournisseurs,
            "achats": achats,
            "reglements": reglements,
            "frais_personnel": frais_personnel,
            "autres_frais": autres_frais,
            "resume_periode": resume_periode_comptable(
                db, groupement=groupement, fournisseur_id=fournisseur_id_int, type_frais=type_frais
            ),
            "par_fournisseur": achats_par_fournisseur(db, mois=mois),
            "par_employe": frais_personnel_par_employe(db),
            "par_periode": frais_personnel_par_periode(db, employe=employe),
            "par_type_frais": autres_frais_par_type(db, mois=mois),
            "mois_disponibles": lister_mois_comptable(db),
            "types_frais_disponibles": lister_types_frais(db),
            "employes_disponibles": lister_employes(db),
            "filtre_mois": mois or "",
            "filtre_fournisseur_id": fournisseur_id_int or "",
            "filtre_type_frais": type_frais or "",
            "filtre_employe": employe or "",
            "filtre_groupement": groupement,
            "themes_ferme": THEMES_RAPPORT_FERME,
        },
    )


# ---------------------------------------------------------------------------
# Fournisseurs
# ---------------------------------------------------------------------------
@router.get("/fournisseurs/nouveau")
def formulaire_fournisseur(request: Request):
    return templates.TemplateResponse(
        "comptable/fournisseur_formulaire.html", {"request": request, "fournisseur": None}
    )


@router.post("/fournisseurs/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_fournisseur(
    nom: str = Form(...),
    contact: str | None = Form(None),
    telephone: str | None = Form(None),
    adresse: str | None = Form(None),
    numero_compte: str | None = Form(None),
    solde_initial: str | None = Form(None),
    date_solde_initial: str | None = Form(None),
    db: Session = Depends(get_db),
):
    db.add(
        models.Fournisseur(
            nom=nom,
            contact=contact,
            telephone=telephone,
            adresse=adresse,
            numero_compte=numero_compte,
            solde_initial=vers_float(solde_initial) or 0,
            date_solde_initial=date.fromisoformat(date_solde_initial) if date_solde_initial else None,
        )
    )
    db.commit()
    return RedirectResponse(url="/comptable?succes=Fournisseur créé#fournisseurs", status_code=303)


@router.get("/fournisseurs/{fournisseur_id}/modifier")
def formulaire_modif_fournisseur(fournisseur_id: int, request: Request, db: Session = Depends(get_db)):
    fournisseur = db.get(models.Fournisseur, fournisseur_id)
    if fournisseur is None:
        raise HTTPException(status_code=404, detail="Fournisseur introuvable")
    return templates.TemplateResponse(
        "comptable/fournisseur_formulaire.html", {"request": request, "fournisseur": fournisseur}
    )


@router.post("/fournisseurs/{fournisseur_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_fournisseur(
    fournisseur_id: int,
    nom: str = Form(...),
    contact: str | None = Form(None),
    telephone: str | None = Form(None),
    adresse: str | None = Form(None),
    numero_compte: str | None = Form(None),
    solde_initial: str | None = Form(None),
    date_solde_initial: str | None = Form(None),
    db: Session = Depends(get_db),
):
    fournisseur = db.get(models.Fournisseur, fournisseur_id)
    if fournisseur is None:
        raise HTTPException(status_code=404, detail="Fournisseur introuvable")
    fournisseur.nom = nom
    fournisseur.contact = contact
    fournisseur.telephone = telephone
    fournisseur.adresse = adresse
    fournisseur.numero_compte = numero_compte
    fournisseur.solde_initial = vers_float(solde_initial) or 0
    fournisseur.date_solde_initial = date.fromisoformat(date_solde_initial) if date_solde_initial else None
    db.commit()
    return RedirectResponse(url="/comptable?succes=Fournisseur mis à jour#fournisseurs", status_code=303)


@router.post("/fournisseurs/{fournisseur_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_fournisseur(fournisseur_id: int, db: Session = Depends(get_db)):
    fournisseur = db.get(models.Fournisseur, fournisseur_id)
    if fournisseur is None:
        raise HTTPException(status_code=404, detail="Fournisseur introuvable")
    for achat in fournisseur.achats:
        supprimer_fichier(achat.facture_path)
    db.delete(fournisseur)
    db.commit()
    return RedirectResponse(url="/comptable?succes=Fournisseur supprimé#fournisseurs", status_code=303)


# ---------------------------------------------------------------------------
# Achats
# ---------------------------------------------------------------------------
@router.get("/achats/nouveau")
def formulaire_achat(request: Request, db: Session = Depends(get_db)):
    fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
    return templates.TemplateResponse(
        "comptable/achat_formulaire.html",
        {"request": request, "achat": None, "fournisseurs": fournisseurs},
    )


@router.post("/achats/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
async def creer_achat(
    request: Request,
    fournisseur_id: int = Form(...),
    date_achat: date = Form(...),
    numero_facture: str | None = Form(None),
    designation_produit: str = Form(...),
    quantite: str | None = Form(None),
    unite: str | None = Form(None),
    prix_unitaire: str | None = Form(None),
    montant_total: float = Form(...),
    facture: UploadFile | None = None,
    db: Session = Depends(get_db),
):
    facture_path = None
    if facture is not None and facture.filename:
        try:
            facture_path = await enregistrer_fichier(facture, "factures_achat")
        except ValueError as exc:
            fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
            return templates.TemplateResponse(
                "comptable/achat_formulaire.html",
                {"request": request, "achat": None, "fournisseurs": fournisseurs, "erreur": str(exc)},
                status_code=400,
            )

    db.add(
        models.Achat(
            fournisseur_id=fournisseur_id,
            date_achat=date_achat,
            numero_facture=numero_facture,
            designation_produit=designation_produit,
            quantite=vers_float(quantite),
            unite=unite,
            prix_unitaire=vers_float(prix_unitaire),
            montant_total=montant_total,
            facture_path=facture_path,
        )
    )
    db.commit()
    return RedirectResponse(url="/comptable?succes=Achat enregistré#achats", status_code=303)


@router.get("/achats/{achat_id}/modifier")
def formulaire_modif_achat(achat_id: int, request: Request, db: Session = Depends(get_db)):
    achat = db.get(models.Achat, achat_id)
    if achat is None:
        raise HTTPException(status_code=404, detail="Achat introuvable")
    fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
    return templates.TemplateResponse(
        "comptable/achat_formulaire.html",
        {"request": request, "achat": achat, "fournisseurs": fournisseurs},
    )


@router.post("/achats/{achat_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
async def modifier_achat(
    achat_id: int,
    request: Request,
    fournisseur_id: int = Form(...),
    date_achat: date = Form(...),
    numero_facture: str | None = Form(None),
    designation_produit: str = Form(...),
    quantite: str | None = Form(None),
    unite: str | None = Form(None),
    prix_unitaire: str | None = Form(None),
    montant_total: float = Form(...),
    facture: UploadFile | None = None,
    db: Session = Depends(get_db),
):
    achat = db.get(models.Achat, achat_id)
    if achat is None:
        raise HTTPException(status_code=404, detail="Achat introuvable")

    if facture is not None and facture.filename:
        try:
            nouvelle_facture = await enregistrer_fichier(facture, "factures_achat")
        except ValueError as exc:
            fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
            return templates.TemplateResponse(
                "comptable/achat_formulaire.html",
                {"request": request, "achat": achat, "fournisseurs": fournisseurs, "erreur": str(exc)},
                status_code=400,
            )
        supprimer_fichier(achat.facture_path)
        achat.facture_path = nouvelle_facture

    achat.fournisseur_id = fournisseur_id
    achat.date_achat = date_achat
    achat.numero_facture = numero_facture
    achat.designation_produit = designation_produit
    achat.quantite = vers_float(quantite)
    achat.unite = unite
    achat.prix_unitaire = vers_float(prix_unitaire)
    achat.montant_total = montant_total
    db.commit()
    return RedirectResponse(url="/comptable?succes=Achat mis à jour#achats", status_code=303)


@router.post("/achats/{achat_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_achat(achat_id: int, db: Session = Depends(get_db)):
    achat = db.get(models.Achat, achat_id)
    if achat is None:
        raise HTTPException(status_code=404, detail="Achat introuvable")
    for reglement in achat.reglements:
        reglement.achat_id = None
    supprimer_fichier(achat.facture_path)
    db.delete(achat)
    db.commit()
    return RedirectResponse(url="/comptable?succes=Achat supprimé#achats", status_code=303)


# ---------------------------------------------------------------------------
# Règlements (paiements aux fournisseurs)
# ---------------------------------------------------------------------------
@router.get("/reglements/nouveau")
def formulaire_reglement(request: Request, fournisseur_id: str | None = None, db: Session = Depends(get_db)):
    fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
    achats = db.query(models.Achat).order_by(models.Achat.date_achat.desc()).all()
    return templates.TemplateResponse(
        "comptable/reglement_formulaire.html",
        {
            "request": request,
            "reglement": None,
            "fournisseurs": fournisseurs,
            "achats": achats,
            "fournisseur_id_preselection": vers_int(fournisseur_id) or "",
        },
    )


@router.post("/reglements/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_reglement(
    fournisseur_id: int = Form(...),
    achat_id: str | None = Form(None),
    date_reglement: date = Form(...),
    mode_paiement: str = Form(...),
    reference: str | None = Form(None),
    montant: float = Form(...),
    notes: str | None = Form(None),
    db: Session = Depends(get_db),
):
    achat_id_int = vers_int(achat_id)
    if achat_id_int is not None:
        achat = db.get(models.Achat, achat_id_int)
        if achat is None or achat.fournisseur_id != fournisseur_id:
            raise HTTPException(status_code=400, detail="Achat invalide pour ce fournisseur")
    db.add(
        models.Reglement(
            fournisseur_id=fournisseur_id,
            achat_id=achat_id_int,
            date_reglement=date_reglement,
            mode_paiement=mode_paiement,
            reference=reference,
            montant=montant,
            notes=notes,
        )
    )
    db.commit()
    return RedirectResponse(url="/comptable?succes=Règlement enregistré#reglements", status_code=303)


@router.get("/reglements/{reglement_id}/modifier")
def formulaire_modif_reglement(reglement_id: int, request: Request, db: Session = Depends(get_db)):
    reglement = db.get(models.Reglement, reglement_id)
    if reglement is None:
        raise HTTPException(status_code=404, detail="Règlement introuvable")
    fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
    achats = db.query(models.Achat).order_by(models.Achat.date_achat.desc()).all()
    return templates.TemplateResponse(
        "comptable/reglement_formulaire.html",
        {
            "request": request,
            "reglement": reglement,
            "fournisseurs": fournisseurs,
            "achats": achats,
            "fournisseur_id_preselection": reglement.fournisseur_id,
        },
    )


@router.post("/reglements/{reglement_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_reglement(
    reglement_id: int,
    fournisseur_id: int = Form(...),
    achat_id: str | None = Form(None),
    date_reglement: date = Form(...),
    mode_paiement: str = Form(...),
    reference: str | None = Form(None),
    montant: float = Form(...),
    notes: str | None = Form(None),
    db: Session = Depends(get_db),
):
    reglement = db.get(models.Reglement, reglement_id)
    if reglement is None:
        raise HTTPException(status_code=404, detail="Règlement introuvable")
    achat_id_int = vers_int(achat_id)
    if achat_id_int is not None:
        achat = db.get(models.Achat, achat_id_int)
        if achat is None or achat.fournisseur_id != fournisseur_id:
            raise HTTPException(status_code=400, detail="Achat invalide pour ce fournisseur")
    reglement.fournisseur_id = fournisseur_id
    reglement.achat_id = achat_id_int
    reglement.date_reglement = date_reglement
    reglement.mode_paiement = mode_paiement
    reglement.reference = reference
    reglement.montant = montant
    reglement.notes = notes
    db.commit()
    return RedirectResponse(url="/comptable?succes=Règlement mis à jour#reglements", status_code=303)


@router.post("/reglements/{reglement_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_reglement(reglement_id: int, db: Session = Depends(get_db)):
    reglement = db.get(models.Reglement, reglement_id)
    if reglement is None:
        raise HTTPException(status_code=404, detail="Règlement introuvable")
    db.delete(reglement)
    db.commit()
    return RedirectResponse(url="/comptable?succes=Règlement supprimé#reglements", status_code=303)


# ---------------------------------------------------------------------------
# Rapport imprimable
# ---------------------------------------------------------------------------
@router.get("/rapports/imprimer")
def rapport_imprimable(
    request: Request,
    theme: str = "tout",
    date_debut: str | None = None,
    date_fin: str | None = None,
    db: Session = Depends(get_db),
):
    if theme not in THEMES_RAPPORT:
        theme = "tout"
    debut = date.fromisoformat(date_debut) if date_debut else date(date.today().year, 1, 1)
    fin = date.fromisoformat(date_fin) if date_fin else date(date.today().year, 12, 31)

    inclure_achats = theme in ("fournisseurs", "tout")
    inclure_autres_frais = theme in ("autres", "personnel_autres", "tout")
    inclure_personnel = theme in ("personnel", "personnel_autres", "tout")

    achats = achats_periode(db, debut, fin) if inclure_achats else []
    par_fournisseur = achats_par_fournisseur_periode(db, debut, fin) if inclure_achats else []
    autres_frais = autres_frais_periode(db, debut, fin) if inclure_autres_frais else []
    par_type_frais = autres_frais_par_type_periode(db, debut, fin) if inclure_autres_frais else []
    frais_personnel = frais_personnel_periode(db, debut, fin) if inclure_personnel else []
    par_employe = frais_personnel_par_employe_periode(db, debut, fin) if inclure_personnel else []

    total_achats = sum(a.montant_total for a in achats)
    total_autres_frais = sum(f.montant for f in autres_frais)
    total_personnel = sum(f.montant for f in frais_personnel)

    return templates.TemplateResponse(
        "comptable/rapport.html",
        {
            "request": request,
            "themes": THEMES_RAPPORT,
            "theme": theme,
            "date_debut": debut,
            "date_fin": fin,
            "inclure_achats": inclure_achats,
            "inclure_autres_frais": inclure_autres_frais,
            "inclure_personnel": inclure_personnel,
            "achats": achats,
            "par_fournisseur": par_fournisseur,
            "autres_frais": autres_frais,
            "par_type_frais": par_type_frais,
            "frais_personnel": frais_personnel,
            "par_employe": par_employe,
            "total_achats": total_achats,
            "total_autres_frais": total_autres_frais,
            "total_personnel": total_personnel,
            "total_general": total_achats + total_autres_frais + total_personnel,
        },
    )


# ---------------------------------------------------------------------------
# Grand livre fournisseur
# ---------------------------------------------------------------------------
@router.get("/grand-livre")
def grand_livre(
    request: Request,
    fournisseur_id: str | None = None,
    date_debut: str | None = None,
    date_fin: str | None = None,
    db: Session = Depends(get_db),
):
    fournisseurs = db.query(models.Fournisseur).order_by(models.Fournisseur.nom).all()
    fournisseur_id_int = vers_int(fournisseur_id)
    fournisseur = db.get(models.Fournisseur, fournisseur_id_int) if fournisseur_id_int else None

    debut = date.fromisoformat(date_debut) if date_debut else date(date.today().year, 1, 1)
    fin = date.fromisoformat(date_fin) if date_fin else date(date.today().year, 12, 31)

    # Pas de fournisseur précis sélectionné ("Tous") : un grand livre par fournisseur, à la suite.
    cibles = [fournisseur] if fournisseur else fournisseurs
    livres = [grand_livre_fournisseur(db, f, debut, fin) for f in cibles]

    return templates.TemplateResponse(
        "comptable/grand_livre.html",
        {
            "request": request,
            "fournisseurs": fournisseurs,
            "fournisseur": fournisseur,
            "livres": livres,
            "date_debut": debut,
            "date_fin": fin,
        },
    )


# ---------------------------------------------------------------------------
# Frais de personnel
# ---------------------------------------------------------------------------
@router.get("/frais-personnel/nouveau")
def formulaire_frais_personnel(request: Request):
    return templates.TemplateResponse(
        "comptable/frais_personnel_formulaire.html", {"request": request, "frais": None}
    )


@router.post("/frais-personnel/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_frais_personnel(
    employe: str = Form(...),
    periode: str = Form(...),
    date_debut: date = Form(...),
    montant: float = Form(...),
    description: str | None = Form(None),
    db: Session = Depends(get_db),
):
    db.add(
        models.FraisPersonnel(
            employe=employe, periode=periode, date_debut=date_debut, montant=montant, description=description
        )
    )
    db.commit()
    return RedirectResponse(url="/comptable?succes=Frais de personnel enregistré#frais-personnel", status_code=303)


@router.get("/frais-personnel/{frais_id}/modifier")
def formulaire_modif_frais_personnel(frais_id: int, request: Request, db: Session = Depends(get_db)):
    frais = db.get(models.FraisPersonnel, frais_id)
    if frais is None:
        raise HTTPException(status_code=404, detail="Frais introuvable")
    return templates.TemplateResponse(
        "comptable/frais_personnel_formulaire.html", {"request": request, "frais": frais}
    )


@router.post("/frais-personnel/{frais_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_frais_personnel(
    frais_id: int,
    employe: str = Form(...),
    periode: str = Form(...),
    date_debut: date = Form(...),
    montant: float = Form(...),
    description: str | None = Form(None),
    db: Session = Depends(get_db),
):
    frais = db.get(models.FraisPersonnel, frais_id)
    if frais is None:
        raise HTTPException(status_code=404, detail="Frais introuvable")
    frais.employe = employe
    frais.periode = periode
    frais.date_debut = date_debut
    frais.montant = montant
    frais.description = description
    db.commit()
    return RedirectResponse(url="/comptable?succes=Frais mis à jour#frais-personnel", status_code=303)


@router.post("/frais-personnel/{frais_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_frais_personnel(frais_id: int, db: Session = Depends(get_db)):
    frais = db.get(models.FraisPersonnel, frais_id)
    if frais is None:
        raise HTTPException(status_code=404, detail="Frais introuvable")
    db.delete(frais)
    db.commit()
    return RedirectResponse(url="/comptable?succes=Frais supprimé#frais-personnel", status_code=303)


# ---------------------------------------------------------------------------
# Autres frais
# ---------------------------------------------------------------------------
@router.get("/autres-frais/nouveau")
def formulaire_autre_frais(request: Request):
    return templates.TemplateResponse(
        "comptable/autre_frais_formulaire.html", {"request": request, "frais": None}
    )


@router.post("/autres-frais/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_autre_frais(
    type_frais: str = Form(...),
    date_frais: date = Form(...),
    montant: float = Form(...),
    description: str | None = Form(None),
    db: Session = Depends(get_db),
):
    db.add(models.AutreFrais(type_frais=type_frais, date_frais=date_frais, montant=montant, description=description))
    db.commit()
    return RedirectResponse(url="/comptable?succes=Frais enregistré#autres-frais", status_code=303)


@router.get("/autres-frais/{frais_id}/modifier")
def formulaire_modif_autre_frais(frais_id: int, request: Request, db: Session = Depends(get_db)):
    frais = db.get(models.AutreFrais, frais_id)
    if frais is None:
        raise HTTPException(status_code=404, detail="Frais introuvable")
    return templates.TemplateResponse(
        "comptable/autre_frais_formulaire.html", {"request": request, "frais": frais}
    )


@router.post("/autres-frais/{frais_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_autre_frais(
    frais_id: int,
    type_frais: str = Form(...),
    date_frais: date = Form(...),
    montant: float = Form(...),
    description: str | None = Form(None),
    db: Session = Depends(get_db),
):
    frais = db.get(models.AutreFrais, frais_id)
    if frais is None:
        raise HTTPException(status_code=404, detail="Frais introuvable")
    frais.type_frais = type_frais
    frais.date_frais = date_frais
    frais.montant = montant
    frais.description = description
    db.commit()
    return RedirectResponse(url="/comptable?succes=Frais mis à jour#autres-frais", status_code=303)


@router.post("/autres-frais/{frais_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_autre_frais(frais_id: int, db: Session = Depends(get_db)):
    frais = db.get(models.AutreFrais, frais_id)
    if frais is None:
        raise HTTPException(status_code=404, detail="Frais introuvable")
    db.delete(frais)
    db.commit()
    return RedirectResponse(url="/comptable?succes=Frais supprimé#autres-frais", status_code=303)
