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.geo import DEVISES, INDICATIFS_TELEPHONE, LISTE_PAYS
from app.rapports import (
    grand_livre_client,
    lister_mois_commercial,
    resume_mensuel_commercial,
    suggerer_numero_document,
    ventes_par_client,
)
from app.securite import verifier_jeton_csrf
from app.traductions import obtenir_traductions
from app.uploads import enregistrer_fichier, supprimer_fichier
from app.utils import vers_float, vers_int

router = APIRouter(prefix="/commercial", tags=["commercial"])
templates = Jinja2Templates(directory="app/templates")

TYPES_DOCUMENT_VALIDES = {"devis", "facture", "avoir"}


@router.get("")
def accueil_commercial(
    request: Request,
    db: Session = Depends(get_db),
    mois: str | None = None,
    client_id: str | None = None,
):
    client_id_int = vers_int(client_id)
    clients = db.query(models.Client).order_by(models.Client.nom).all()
    ventes = db.query(models.VenteClient).order_by(models.VenteClient.date_vente.desc()).all()
    encaissements = db.query(models.Encaissement).order_by(models.Encaissement.date_encaissement.desc()).all()
    produits = db.query(models.Produit).order_by(models.Produit.nom).all()
    vendeurs = db.query(models.ParametresEntreprise).order_by(models.ParametresEntreprise.nom_societe).all()
    return templates.TemplateResponse(
        "commercial/accueil.html",
        {
            "request": request,
            "clients": clients,
            "ventes": ventes,
            "encaissements": encaissements,
            "produits": produits,
            "vendeurs": vendeurs,
            "resume_mensuel": resume_mensuel_commercial(db, client_id=client_id_int),
            "par_client": ventes_par_client(db, mois=mois),
            "mois_disponibles": lister_mois_commercial(db),
            "filtre_mois": mois or "",
            "filtre_client_id": client_id_int or "",
        },
    )


# ---------------------------------------------------------------------------
# Clients
# ---------------------------------------------------------------------------
@router.get("/clients/nouveau")
def formulaire_client(request: Request):
    return templates.TemplateResponse(
        "commercial/client_formulaire.html",
        {
            "request": request, "client": None,
            "pays_disponibles": LISTE_PAYS, "indicatifs": INDICATIFS_TELEPHONE, "devises": DEVISES,
        },
    )


@router.post("/clients/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_client(
    nom: str = Form(...),
    contact: str | None = Form(None),
    telephone: str | None = Form(None),
    telephone_portable: str | None = Form(None),
    email: str | None = Form(None),
    numero_rue: str | None = Form(None),
    adresse: str | None = Form(None),
    code_postal: str | None = Form(None),
    pays: str | None = Form(None),
    devise: str | None = Form(None),
    registre_commerce: str | None = Form(None),
    autres_infos_legales: str | None = Form(None),
    afficher_telephone_bureau: str | None = Form(None),
    afficher_telephone_portable: str | None = Form(None),
    afficher_email: str | None = Form(None),
    afficher_registre_commerce: str | None = Form(None),
    afficher_autres_infos: 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.Client(
            nom=nom,
            contact=contact,
            telephone=telephone,
            telephone_portable=telephone_portable,
            email=email,
            numero_rue=numero_rue,
            adresse=adresse,
            code_postal=code_postal,
            pays=pays,
            devise=(devise or "MAD").strip() or "MAD",
            registre_commerce=registre_commerce,
            autres_infos_legales=autres_infos_legales,
            afficher_telephone_bureau=afficher_telephone_bureau == "on",
            afficher_telephone_portable=afficher_telephone_portable == "on",
            afficher_email=afficher_email == "on",
            afficher_registre_commerce=afficher_registre_commerce == "on",
            afficher_autres_infos=afficher_autres_infos == "on",
            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="/commercial?succes=Client créé#clients", status_code=303)


@router.get("/clients/{client_id}/modifier")
def formulaire_modif_client(client_id: int, request: Request, db: Session = Depends(get_db)):
    client = db.get(models.Client, client_id)
    if client is None:
        raise HTTPException(status_code=404, detail="Client introuvable")
    return templates.TemplateResponse(
        "commercial/client_formulaire.html",
        {
            "request": request, "client": client,
            "pays_disponibles": LISTE_PAYS, "indicatifs": INDICATIFS_TELEPHONE, "devises": DEVISES,
        },
    )


@router.post("/clients/{client_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_client(
    client_id: int,
    nom: str = Form(...),
    contact: str | None = Form(None),
    telephone: str | None = Form(None),
    telephone_portable: str | None = Form(None),
    email: str | None = Form(None),
    numero_rue: str | None = Form(None),
    adresse: str | None = Form(None),
    code_postal: str | None = Form(None),
    pays: str | None = Form(None),
    devise: str | None = Form(None),
    registre_commerce: str | None = Form(None),
    autres_infos_legales: str | None = Form(None),
    afficher_telephone_bureau: str | None = Form(None),
    afficher_telephone_portable: str | None = Form(None),
    afficher_email: str | None = Form(None),
    afficher_registre_commerce: str | None = Form(None),
    afficher_autres_infos: str | None = Form(None),
    solde_initial: str | None = Form(None),
    date_solde_initial: str | None = Form(None),
    db: Session = Depends(get_db),
):
    client = db.get(models.Client, client_id)
    if client is None:
        raise HTTPException(status_code=404, detail="Client introuvable")
    client.nom = nom
    client.contact = contact
    client.telephone = telephone
    client.telephone_portable = telephone_portable
    client.email = email
    client.numero_rue = numero_rue
    client.adresse = adresse
    client.code_postal = code_postal
    client.pays = pays
    client.devise = (devise or "MAD").strip() or "MAD"
    client.registre_commerce = registre_commerce
    client.autres_infos_legales = autres_infos_legales
    client.afficher_telephone_bureau = afficher_telephone_bureau == "on"
    client.afficher_telephone_portable = afficher_telephone_portable == "on"
    client.afficher_email = afficher_email == "on"
    client.afficher_registre_commerce = afficher_registre_commerce == "on"
    client.afficher_autres_infos = afficher_autres_infos == "on"
    client.solde_initial = vers_float(solde_initial) or 0
    client.date_solde_initial = date.fromisoformat(date_solde_initial) if date_solde_initial else None
    db.commit()
    return RedirectResponse(url="/commercial?succes=Client mis à jour#clients", status_code=303)


@router.post("/clients/{client_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_client(client_id: int, db: Session = Depends(get_db)):
    client = db.get(models.Client, client_id)
    if client is None:
        raise HTTPException(status_code=404, detail="Client introuvable")
    for vente in client.ventes:
        supprimer_fichier(vente.facture_vente_path)
    db.delete(client)
    db.commit()
    return RedirectResponse(url="/commercial?succes=Client supprimé#clients", status_code=303)


# ---------------------------------------------------------------------------
# Produits (catalogue)
# ---------------------------------------------------------------------------
@router.get("/produits/nouveau")
def formulaire_produit(request: Request):
    return templates.TemplateResponse("commercial/produit_formulaire.html", {"request": request, "produit": None})


@router.post("/produits/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_produit(
    nom: str = Form(...),
    description: str | None = Form(None),
    unite: str | None = Form(None),
    prix_unitaire_defaut: str | None = Form(None),
    type_emballage_defaut: str | None = Form(None),
    poids_colis_kg_defaut: str | None = Form(None),
    db: Session = Depends(get_db),
):
    db.add(
        models.Produit(
            nom=nom,
            description=description,
            unite=unite,
            prix_unitaire_defaut=vers_float(prix_unitaire_defaut),
            type_emballage_defaut=type_emballage_defaut,
            poids_colis_kg_defaut=vers_float(poids_colis_kg_defaut),
        )
    )
    db.commit()
    return RedirectResponse(url="/commercial?succes=Produit créé#produits", status_code=303)


@router.get("/produits/{produit_id}/modifier")
def formulaire_modif_produit(produit_id: int, request: Request, db: Session = Depends(get_db)):
    produit = db.get(models.Produit, produit_id)
    if produit is None:
        raise HTTPException(status_code=404, detail="Produit introuvable")
    return templates.TemplateResponse("commercial/produit_formulaire.html", {"request": request, "produit": produit})


@router.post("/produits/{produit_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_produit(
    produit_id: int,
    nom: str = Form(...),
    description: str | None = Form(None),
    unite: str | None = Form(None),
    prix_unitaire_defaut: str | None = Form(None),
    type_emballage_defaut: str | None = Form(None),
    poids_colis_kg_defaut: str | None = Form(None),
    actif: str | None = Form(None),
    db: Session = Depends(get_db),
):
    produit = db.get(models.Produit, produit_id)
    if produit is None:
        raise HTTPException(status_code=404, detail="Produit introuvable")
    produit.nom = nom
    produit.description = description
    produit.unite = unite
    produit.prix_unitaire_defaut = vers_float(prix_unitaire_defaut)
    produit.type_emballage_defaut = type_emballage_defaut
    produit.poids_colis_kg_defaut = vers_float(poids_colis_kg_defaut)
    produit.actif = actif == "on"
    db.commit()
    return RedirectResponse(url="/commercial?succes=Produit mis à jour#produits", status_code=303)


@router.post("/produits/{produit_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_produit(produit_id: int, db: Session = Depends(get_db)):
    produit = db.get(models.Produit, produit_id)
    if produit is None:
        raise HTTPException(status_code=404, detail="Produit introuvable")
    db.delete(produit)
    db.commit()
    return RedirectResponse(url="/commercial?succes=Produit supprimé#produits", status_code=303)


# ---------------------------------------------------------------------------
# Vendeurs (entités de facturation : nom, adresse, RC/ICE, banque, logo)
# ---------------------------------------------------------------------------
@router.get("/vendeurs/nouveau")
def formulaire_vendeur(request: Request):
    return templates.TemplateResponse("commercial/vendeur_formulaire.html", {"request": request, "vendeur": None})


@router.post("/vendeurs/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
async def creer_vendeur(
    request: Request,
    nom_societe: str = Form(...),
    adresse_ligne1: str | None = Form(None),
    adresse_ligne2: str | None = Form(None),
    telephone: str | None = Form(None),
    email: str | None = Form(None),
    rc: str | None = Form(None),
    ice: str | None = Form(None),
    banque_nom: str | None = Form(None),
    banque_iban: str | None = Form(None),
    banque_swift: str | None = Form(None),
    logo: UploadFile | None = None,
    db: Session = Depends(get_db),
):
    logo_path = None
    if logo is not None and logo.filename:
        try:
            logo_path = await enregistrer_fichier(logo, "entreprise")
        except ValueError as exc:
            return templates.TemplateResponse(
                "commercial/vendeur_formulaire.html",
                {"request": request, "vendeur": None, "erreur": str(exc)},
                status_code=400,
            )
    db.add(
        models.ParametresEntreprise(
            nom_societe=nom_societe,
            adresse_ligne1=adresse_ligne1,
            adresse_ligne2=adresse_ligne2,
            telephone=telephone,
            email=email,
            rc=rc,
            ice=ice,
            banque_nom=banque_nom,
            banque_iban=banque_iban,
            banque_swift=banque_swift,
            logo_path=logo_path,
        )
    )
    db.commit()
    return RedirectResponse(url="/commercial?succes=Vendeur créé#vendeurs", status_code=303)


@router.get("/vendeurs/{vendeur_id}/modifier")
def formulaire_modif_vendeur(vendeur_id: int, request: Request, db: Session = Depends(get_db)):
    vendeur = db.get(models.ParametresEntreprise, vendeur_id)
    if vendeur is None:
        raise HTTPException(status_code=404, detail="Vendeur introuvable")
    return templates.TemplateResponse("commercial/vendeur_formulaire.html", {"request": request, "vendeur": vendeur})


@router.post("/vendeurs/{vendeur_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
async def modifier_vendeur(
    vendeur_id: int,
    request: Request,
    nom_societe: str = Form(...),
    adresse_ligne1: str | None = Form(None),
    adresse_ligne2: str | None = Form(None),
    telephone: str | None = Form(None),
    email: str | None = Form(None),
    rc: str | None = Form(None),
    ice: str | None = Form(None),
    banque_nom: str | None = Form(None),
    banque_iban: str | None = Form(None),
    banque_swift: str | None = Form(None),
    logo: UploadFile | None = None,
    db: Session = Depends(get_db),
):
    vendeur = db.get(models.ParametresEntreprise, vendeur_id)
    if vendeur is None:
        raise HTTPException(status_code=404, detail="Vendeur introuvable")

    if logo is not None and logo.filename:
        try:
            nouveau_logo = await enregistrer_fichier(logo, "entreprise")
        except ValueError as exc:
            return templates.TemplateResponse(
                "commercial/vendeur_formulaire.html",
                {"request": request, "vendeur": vendeur, "erreur": str(exc)},
                status_code=400,
            )
        supprimer_fichier(vendeur.logo_path)
        vendeur.logo_path = nouveau_logo

    vendeur.nom_societe = nom_societe
    vendeur.adresse_ligne1 = adresse_ligne1
    vendeur.adresse_ligne2 = adresse_ligne2
    vendeur.telephone = telephone
    vendeur.email = email
    vendeur.rc = rc
    vendeur.ice = ice
    vendeur.banque_nom = banque_nom
    vendeur.banque_iban = banque_iban
    vendeur.banque_swift = banque_swift
    db.commit()
    return RedirectResponse(url="/commercial?succes=Vendeur mis à jour#vendeurs", status_code=303)


@router.post("/vendeurs/{vendeur_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_vendeur(vendeur_id: int, db: Session = Depends(get_db)):
    vendeur = db.get(models.ParametresEntreprise, vendeur_id)
    if vendeur is None:
        raise HTTPException(status_code=404, detail="Vendeur introuvable")
    for vente in db.query(models.VenteClient).filter(models.VenteClient.vendeur_id == vendeur_id).all():
        vente.vendeur_id = None
    supprimer_fichier(vendeur.logo_path)
    db.delete(vendeur)
    db.commit()
    return RedirectResponse(url="/commercial?succes=Vendeur supprimé#vendeurs", status_code=303)


# ---------------------------------------------------------------------------
# Ventes / Devis / Factures / Avoirs
# ---------------------------------------------------------------------------
@router.get("/ventes/nouveau")
def formulaire_vente(
    request: Request,
    type: str = "facture",
    origine_id: int | None = None,
    db: Session = Depends(get_db),
):
    type_document = type if type in TYPES_DOCUMENT_VALIDES else "facture"
    clients = db.query(models.Client).order_by(models.Client.nom).all()
    vendeurs = db.query(models.ParametresEntreprise).order_by(models.ParametresEntreprise.nom_societe).all()
    produits = db.query(models.Produit).filter(models.Produit.actif.is_(True)).order_by(models.Produit.nom).all()
    origine = db.get(models.VenteClient, origine_id) if origine_id else None
    numeros_suggeres = {t: suggerer_numero_document(db, t) for t in TYPES_DOCUMENT_VALIDES}
    return templates.TemplateResponse(
        "commercial/vente_formulaire.html",
        {
            "request": request,
            "vente": None,
            "clients": clients,
            "vendeurs": vendeurs,
            "produits": produits,
            "type_document": type_document,
            "numero_suggere": numeros_suggeres[type_document],
            "numeros_suggeres": numeros_suggeres,
            "origine": origine,
        },
    )


@router.post("/ventes/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
async def creer_vente(
    request: Request,
    client_id: int = Form(...),
    vendeur_id: str | None = Form(None),
    type_document: str = Form("facture"),
    facture_origine_id: str | None = Form(None),
    date_vente: date = Form(...),
    date_echeance: str | None = Form(None),
    numero_facture: str | None = Form(None),
    mode_reglement: str | None = Form(None),
    produit: str | None = Form(None),
    quantite: str | None = Form(None),
    montant: str | None = Form(None),
    transporteur: str | None = Form(None),
    matricule_camion: str | None = Form(None),
    incoterm: str | None = Form(None),
    facture_vente: UploadFile | None = None,
    ligne_produit: list[str] = Form([]),
    ligne_nb_palettes: list[str] = Form([]),
    ligne_type_emballage: list[str] = Form([]),
    ligne_nombre_colis: list[str] = Form([]),
    ligne_poids_brut_kg: list[str] = Form([]),
    ligne_poids_net_kg: list[str] = Form([]),
    ligne_prix_unitaire: list[str] = Form([]),
    ligne_montant: list[str] = Form([]),
    db: Session = Depends(get_db),
):
    if type_document not in TYPES_DOCUMENT_VALIDES:
        type_document = "facture"

    lignes_valides = [
        {
            "produit": p.strip(),
            "nb_palettes": vers_int(ligne_nb_palettes[i]) if i < len(ligne_nb_palettes) else None,
            "type_emballage": ligne_type_emballage[i] if i < len(ligne_type_emballage) else None,
            "nombre_colis": vers_int(ligne_nombre_colis[i]) if i < len(ligne_nombre_colis) else None,
            "poids_brut_kg": vers_float(ligne_poids_brut_kg[i]) if i < len(ligne_poids_brut_kg) else None,
            "poids_net_kg": vers_float(ligne_poids_net_kg[i]) if i < len(ligne_poids_net_kg) else None,
            "prix_unitaire": vers_float(ligne_prix_unitaire[i]) if i < len(ligne_prix_unitaire) else None,
            "montant": vers_float(ligne_montant[i]) if i < len(ligne_montant) else 0,
        }
        for i, p in enumerate(ligne_produit)
        if p.strip()
    ]

    if not lignes_valides and (not produit or not produit.strip() or vers_float(montant) is None):
        clients = db.query(models.Client).order_by(models.Client.nom).all()
        vendeurs = db.query(models.ParametresEntreprise).order_by(models.ParametresEntreprise.nom_societe).all()
        produits_catalogue = db.query(models.Produit).filter(models.Produit.actif.is_(True)).order_by(models.Produit.nom).all()
        numeros_suggeres = {t: suggerer_numero_document(db, t) for t in TYPES_DOCUMENT_VALIDES}
        return templates.TemplateResponse(
            "commercial/vente_formulaire.html",
            {
                "request": request, "vente": None, "clients": clients, "vendeurs": vendeurs,
                "produits": produits_catalogue,
                "erreur": "Ajoutez au moins une ligne de produit, ou renseignez le produit et le montant.",
                "type_document": type_document, "numero_suggere": numero_facture,
                "numeros_suggeres": numeros_suggeres, "origine": None,
            },
            status_code=400,
        )

    facture_path = None
    if facture_vente is not None and facture_vente.filename:
        try:
            facture_path = await enregistrer_fichier(facture_vente, "factures_vente")
        except ValueError as exc:
            clients = db.query(models.Client).order_by(models.Client.nom).all()
            vendeurs = db.query(models.ParametresEntreprise).order_by(models.ParametresEntreprise.nom_societe).all()
            produits_catalogue = db.query(models.Produit).filter(models.Produit.actif.is_(True)).order_by(models.Produit.nom).all()
            numeros_suggeres = {t: suggerer_numero_document(db, t) for t in TYPES_DOCUMENT_VALIDES}
            return templates.TemplateResponse(
                "commercial/vente_formulaire.html",
                {
                    "request": request, "vente": None, "clients": clients, "vendeurs": vendeurs,
                    "produits": produits_catalogue, "erreur": str(exc),
                    "type_document": type_document, "numero_suggere": numero_facture,
                    "numeros_suggeres": numeros_suggeres, "origine": None,
                },
                status_code=400,
            )

    if lignes_valides:
        produit_resume = lignes_valides[0]["produit"]
        montant_total = round(sum(l["montant"] or 0 for l in lignes_valides), 2)
    else:
        produit_resume = produit.strip()
        montant_total = vers_float(montant) or 0

    vente = models.VenteClient(
        client_id=client_id,
        vendeur_id=vers_int(vendeur_id),
        type_document=type_document,
        facture_origine_id=vers_int(facture_origine_id),
        date_vente=date_vente,
        date_echeance=date.fromisoformat(date_echeance) if date_echeance else None,
        numero_facture=numero_facture,
        mode_reglement=mode_reglement,
        produit=produit_resume,
        quantite=vers_float(quantite),
        montant=montant_total,
        facture_vente_path=facture_path,
        transporteur=transporteur,
        matricule_camion=matricule_camion,
        incoterm=incoterm,
    )
    db.add(vente)
    db.flush()
    for ligne in lignes_valides:
        db.add(models.LigneVente(vente_id=vente.id, **ligne))
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{vente.id}?succes=Document créé", status_code=303)


@router.get("/ventes/{vente_id}")
def detail_vente(vente_id: int, request: Request, db: Session = Depends(get_db)):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Vente introuvable")
    produits = db.query(models.Produit).filter(models.Produit.actif.is_(True)).order_by(models.Produit.nom).all()
    return templates.TemplateResponse(
        "commercial/vente_detail.html", {"request": request, "vente": vente, "produits": produits}
    )


@router.get("/ventes/{vente_id}/imprimer")
def imprimer_facture(vente_id: int, request: Request, langue: str = "fr", db: Session = Depends(get_db)):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Vente introuvable")
    entreprise = vente.vendeur or db.query(models.ParametresEntreprise).first()
    totaux = {
        "nb_palettes": sum(l.nb_palettes or 0 for l in vente.lignes),
        "nombre_colis": sum(l.nombre_colis or 0 for l in vente.lignes),
        "poids_brut_kg": sum(l.poids_brut_kg or 0 for l in vente.lignes),
        "poids_net_kg": sum(l.poids_net_kg or 0 for l in vente.lignes),
    }
    return templates.TemplateResponse(
        "commercial/facture_imprimer.html",
        {
            "request": request, "vente": vente, "entreprise": entreprise, "totaux": totaux,
            "langue": langue, "t": obtenir_traductions(langue),
        },
    )


@router.get("/ventes/{vente_id}/modifier")
def formulaire_modif_vente(vente_id: int, request: Request, db: Session = Depends(get_db)):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Vente introuvable")
    clients = db.query(models.Client).order_by(models.Client.nom).all()
    vendeurs = db.query(models.ParametresEntreprise).order_by(models.ParametresEntreprise.nom_societe).all()
    return templates.TemplateResponse(
        "commercial/vente_formulaire.html",
        {
            "request": request, "vente": vente, "clients": clients, "vendeurs": vendeurs,
            "type_document": vente.type_document, "numero_suggere": vente.numero_facture, "origine": vente.facture_origine,
        },
    )


@router.post("/ventes/{vente_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
async def modifier_vente(
    vente_id: int,
    request: Request,
    client_id: int = Form(...),
    vendeur_id: str | None = Form(None),
    date_vente: date = Form(...),
    date_echeance: str | None = Form(None),
    numero_facture: str | None = Form(None),
    mode_reglement: str | None = Form(None),
    produit: str = Form(...),
    quantite: str | None = Form(None),
    montant: float = Form(...),
    transporteur: str | None = Form(None),
    matricule_camion: str | None = Form(None),
    incoterm: str | None = Form(None),
    facture_vente: UploadFile | None = None,
    db: Session = Depends(get_db),
):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Vente introuvable")

    if facture_vente is not None and facture_vente.filename:
        try:
            nouvelle_facture = await enregistrer_fichier(facture_vente, "factures_vente")
        except ValueError as exc:
            clients = db.query(models.Client).order_by(models.Client.nom).all()
            vendeurs = db.query(models.ParametresEntreprise).order_by(models.ParametresEntreprise.nom_societe).all()
            return templates.TemplateResponse(
                "commercial/vente_formulaire.html",
                {
                    "request": request, "vente": vente, "clients": clients, "vendeurs": vendeurs, "erreur": str(exc),
                    "type_document": vente.type_document, "numero_suggere": vente.numero_facture, "origine": vente.facture_origine,
                },
                status_code=400,
            )
        supprimer_fichier(vente.facture_vente_path)
        vente.facture_vente_path = nouvelle_facture

    vente.client_id = client_id
    vente.vendeur_id = vers_int(vendeur_id)
    vente.date_vente = date_vente
    vente.date_echeance = date.fromisoformat(date_echeance) if date_echeance else None
    vente.numero_facture = numero_facture
    vente.mode_reglement = mode_reglement
    vente.produit = produit
    vente.quantite = vers_float(quantite)
    if not vente.lignes:
        # Le montant n'est modifiable à la main que pour un document simple sans lignes
        # détaillées : dès qu'il y a des lignes, le montant est recalculé automatiquement.
        vente.montant = montant
    vente.transporteur = transporteur
    vente.matricule_camion = matricule_camion
    vente.incoterm = incoterm
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{vente_id}?succes=Document mis à jour", status_code=303)


@router.post("/ventes/{vente_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_vente(vente_id: int, db: Session = Depends(get_db)):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Vente introuvable")
    for encaissement in vente.encaissements:
        encaissement.vente_id = None
    for avoir in db.query(models.VenteClient).filter(models.VenteClient.facture_origine_id == vente_id).all():
        avoir.facture_origine_id = None
    supprimer_fichier(vente.facture_vente_path)
    db.delete(vente)
    db.commit()
    return RedirectResponse(url="/commercial?succes=Document supprimé#ventes", status_code=303)


@router.post("/ventes/{vente_id}/transformer-en-facture", dependencies=[Depends(verifier_jeton_csrf)])
def transformer_en_facture(vente_id: int, db: Session = Depends(get_db)):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Devis introuvable")
    if vente.type_document != "devis":
        raise HTTPException(status_code=400, detail="Seul un devis peut être transformé en facture")
    vente.type_document = "facture"
    vente.numero_facture = suggerer_numero_document(db, "facture")
    vente.date_vente = date.today()
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{vente_id}?succes=Devis transformé en facture", status_code=303)


@router.post("/ventes/{vente_id}/creer-avoir", dependencies=[Depends(verifier_jeton_csrf)])
def creer_avoir(vente_id: int, db: Session = Depends(get_db)):
    facture = db.get(models.VenteClient, vente_id)
    if facture is None:
        raise HTTPException(status_code=404, detail="Facture introuvable")
    if facture.type_document != "facture":
        raise HTTPException(status_code=400, detail="Un avoir ne peut être créé que depuis une facture")
    avoir = models.VenteClient(
        client_id=facture.client_id,
        type_document="avoir",
        facture_origine_id=facture.id,
        date_vente=date.today(),
        numero_facture=suggerer_numero_document(db, "avoir"),
        produit=f"Avoir sur {facture.numero_facture or ('facture #' + str(facture.id))}",
        montant=0,
    )
    db.add(avoir)
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{avoir.id}?succes=Avoir créé, ajoutez les lignes concernées", status_code=303)


@router.post("/ventes/{vente_id}/dupliquer", dependencies=[Depends(verifier_jeton_csrf)])
def dupliquer_vente(vente_id: int, db: Session = Depends(get_db)):
    original = db.get(models.VenteClient, vente_id)
    if original is None:
        raise HTTPException(status_code=404, detail="Document introuvable")
    copie = models.VenteClient(
        client_id=original.client_id,
        type_document=original.type_document,
        date_vente=date.today(),
        numero_facture=suggerer_numero_document(db, original.type_document),
        produit=original.produit,
        quantite=original.quantite,
        montant=original.montant,
        transporteur=original.transporteur,
        matricule_camion=original.matricule_camion,
        incoterm=original.incoterm,
    )
    db.add(copie)
    db.flush()
    for ligne in original.lignes:
        db.add(
            models.LigneVente(
                vente_id=copie.id,
                produit=ligne.produit,
                nb_palettes=ligne.nb_palettes,
                type_emballage=ligne.type_emballage,
                nombre_colis=ligne.nombre_colis,
                poids_brut_kg=ligne.poids_brut_kg,
                poids_net_kg=ligne.poids_net_kg,
                prix_unitaire=ligne.prix_unitaire,
                montant=ligne.montant,
            )
        )
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{copie.id}?succes=Document dupliqué", status_code=303)


# ---------------------------------------------------------------------------
# Lignes de facture (produit, nb palettes, emballage, colis, poids, prix)
# ---------------------------------------------------------------------------
def _recalculer_montant(vente: models.VenteClient) -> None:
    if vente.lignes:
        vente.montant = round(sum(ligne.montant for ligne in vente.lignes), 2)


@router.post("/ventes/{vente_id}/lignes", dependencies=[Depends(verifier_jeton_csrf)])
def ajouter_ligne_vente(
    vente_id: int,
    produit: str = Form(...),
    nb_palettes: str | None = Form(None),
    type_emballage: str | None = Form(None),
    nombre_colis: str | None = Form(None),
    poids_brut_kg: str | None = Form(None),
    poids_net_kg: str | None = Form(None),
    prix_unitaire: str | None = Form(None),
    montant: float = Form(...),
    db: Session = Depends(get_db),
):
    vente = db.get(models.VenteClient, vente_id)
    if vente is None:
        raise HTTPException(status_code=404, detail="Vente introuvable")

    db.add(
        models.LigneVente(
            vente_id=vente_id,
            produit=produit,
            nb_palettes=vers_int(nb_palettes),
            type_emballage=type_emballage,
            nombre_colis=vers_int(nombre_colis),
            poids_brut_kg=vers_float(poids_brut_kg),
            poids_net_kg=vers_float(poids_net_kg),
            prix_unitaire=vers_float(prix_unitaire),
            montant=montant,
        )
    )
    db.flush()
    db.refresh(vente)
    _recalculer_montant(vente)
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{vente_id}?succes=Ligne ajoutée", status_code=303)


@router.post("/lignes/{ligne_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_ligne_vente(ligne_id: int, db: Session = Depends(get_db)):
    ligne = db.get(models.LigneVente, ligne_id)
    if ligne is None:
        raise HTTPException(status_code=404, detail="Ligne introuvable")
    vente_id = ligne.vente_id
    db.delete(ligne)
    db.flush()
    vente = db.get(models.VenteClient, vente_id)
    _recalculer_montant(vente)
    db.commit()
    return RedirectResponse(url=f"/commercial/ventes/{vente_id}?succes=Ligne supprimée", status_code=303)


# ---------------------------------------------------------------------------
# Encaissements (paiements des clients)
# ---------------------------------------------------------------------------
@router.get("/encaissements/nouveau")
def formulaire_encaissement(request: Request, client_id: str | None = None, db: Session = Depends(get_db)):
    clients = db.query(models.Client).order_by(models.Client.nom).all()
    ventes = db.query(models.VenteClient).filter(models.VenteClient.type_document == "facture").order_by(models.VenteClient.date_vente.desc()).all()
    return templates.TemplateResponse(
        "commercial/encaissement_formulaire.html",
        {
            "request": request,
            "encaissement": None,
            "clients": clients,
            "ventes": ventes,
            "client_id_preselection": vers_int(client_id) or "",
        },
    )


@router.post("/encaissements/nouveau", dependencies=[Depends(verifier_jeton_csrf)])
def creer_encaissement(
    client_id: int = Form(...),
    vente_id: str | None = Form(None),
    date_encaissement: date = Form(...),
    mode_paiement: str = Form(...),
    reference: str | None = Form(None),
    montant: float = Form(...),
    notes: str | None = Form(None),
    db: Session = Depends(get_db),
):
    vente_id_int = vers_int(vente_id)
    if vente_id_int is not None:
        vente = db.get(models.VenteClient, vente_id_int)
        if vente is None or vente.client_id != client_id:
            raise HTTPException(status_code=400, detail="Vente invalide pour ce client")
    db.add(
        models.Encaissement(
            client_id=client_id,
            vente_id=vente_id_int,
            date_encaissement=date_encaissement,
            mode_paiement=mode_paiement,
            reference=reference,
            montant=montant,
            notes=notes,
        )
    )
    db.commit()
    return RedirectResponse(url="/commercial?succes=Encaissement enregistré#encaissements", status_code=303)


@router.get("/encaissements/{encaissement_id}/modifier")
def formulaire_modif_encaissement(encaissement_id: int, request: Request, db: Session = Depends(get_db)):
    encaissement = db.get(models.Encaissement, encaissement_id)
    if encaissement is None:
        raise HTTPException(status_code=404, detail="Encaissement introuvable")
    clients = db.query(models.Client).order_by(models.Client.nom).all()
    ventes = db.query(models.VenteClient).filter(models.VenteClient.type_document == "facture").order_by(models.VenteClient.date_vente.desc()).all()
    return templates.TemplateResponse(
        "commercial/encaissement_formulaire.html",
        {
            "request": request,
            "encaissement": encaissement,
            "clients": clients,
            "ventes": ventes,
            "client_id_preselection": encaissement.client_id,
        },
    )


@router.post("/encaissements/{encaissement_id}/modifier", dependencies=[Depends(verifier_jeton_csrf)])
def modifier_encaissement(
    encaissement_id: int,
    client_id: int = Form(...),
    vente_id: str | None = Form(None),
    date_encaissement: date = Form(...),
    mode_paiement: str = Form(...),
    reference: str | None = Form(None),
    montant: float = Form(...),
    notes: str | None = Form(None),
    db: Session = Depends(get_db),
):
    encaissement = db.get(models.Encaissement, encaissement_id)
    if encaissement is None:
        raise HTTPException(status_code=404, detail="Encaissement introuvable")
    vente_id_int = vers_int(vente_id)
    if vente_id_int is not None:
        vente = db.get(models.VenteClient, vente_id_int)
        if vente is None or vente.client_id != client_id:
            raise HTTPException(status_code=400, detail="Vente invalide pour ce client")
    encaissement.client_id = client_id
    encaissement.vente_id = vente_id_int
    encaissement.date_encaissement = date_encaissement
    encaissement.mode_paiement = mode_paiement
    encaissement.reference = reference
    encaissement.montant = montant
    encaissement.notes = notes
    db.commit()
    return RedirectResponse(url="/commercial?succes=Encaissement mis à jour#encaissements", status_code=303)


@router.post("/encaissements/{encaissement_id}/supprimer", dependencies=[Depends(verifier_jeton_csrf)])
def supprimer_encaissement(encaissement_id: int, db: Session = Depends(get_db)):
    encaissement = db.get(models.Encaissement, encaissement_id)
    if encaissement is None:
        raise HTTPException(status_code=404, detail="Encaissement introuvable")
    db.delete(encaissement)
    db.commit()
    return RedirectResponse(url="/commercial?succes=Encaissement supprimé#encaissements", status_code=303)


# ---------------------------------------------------------------------------
# Grand livre client
# ---------------------------------------------------------------------------
@router.get("/grand-livre")
def grand_livre(
    request: Request,
    client_id: str | None = None,
    date_debut: str | None = None,
    date_fin: str | None = None,
    db: Session = Depends(get_db),
):
    clients = db.query(models.Client).order_by(models.Client.nom).all()
    client_id_int = vers_int(client_id)
    client = db.get(models.Client, client_id_int) if client_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 client précis sélectionné ("Tous") : un grand livre par client, à la suite.
    cibles = [client] if client else clients
    livres = [grand_livre_client(db, c, debut, fin) for c in cibles]

    return templates.TemplateResponse(
        "commercial/grand_livre.html",
        {
            "request": request,
            "clients": clients,
            "client": client,
            "livres": livres,
            "date_debut": debut,
            "date_fin": fin,
        },
    )
