import os
from datetime import date
from pathlib import Path

from fastapi import Depends, FastAPI, HTTPException, Request
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from sqlalchemy import func
from sqlalchemy.orm import Session
from starlette.middleware.sessions import SessionMiddleware

from app import models
from app.config import CONFIG_PATH, GOOGLE_TOKEN_PATH
from app.database import DB_PATH, get_db, init_db
from app.permissions import verrouiller_fichier
from app.routers import auth, comptable, commercial, parametres, station, terrains, utilisateurs
from app.securite import AccesRefuse, exiger_connexion, injecter_contexte, obtenir_cle_secrete

BASE_DIR = Path(__file__).resolve().parent
UPLOADS_DIR = BASE_DIR / "static" / "uploads"

# injecter_contexte est une dépendance FastAPI (pas un BaseHTTPMiddleware) appliquée à
# toutes les routes : elle expose request.state.utilisateur/csrf_token aux templates.
# Un BaseHTTPMiddleware a été testé mais désynchronisait request.session sur les POST
# (l'app interne tourne dans une tâche asyncio séparée) et déconnectait les utilisateurs.
app = FastAPI(title="ELFIRMA", dependencies=[Depends(injecter_contexte)])

# Seuls les fichiers non sensibles (CSS) sont servis publiquement. Les fichiers
# uploadés (photos, plans, factures) passent par la route protégée /fichiers.
app.mount("/static/css", StaticFiles(directory=BASE_DIR / "static" / "css"), name="static_css")
templates = Jinja2Templates(directory=BASE_DIR / "templates")

# En production (ELFIRMA_ENV=production, ex. hébergement OVH servi en HTTPS), le cookie
# de session est marqué Secure. En local (http://127.0.0.1), il doit rester non-Secure
# sinon le navigateur refuse de l'enregistrer.
_HTTPS_ONLY = os.environ.get("ELFIRMA_ENV") == "production"
app.add_middleware(SessionMiddleware, secret_key=obtenir_cle_secrete(), same_site="lax", https_only=_HTTPS_ONLY)


@app.exception_handler(AccesRefuse)
def gerer_acces_refuse(request: Request, exc: AccesRefuse):
    return RedirectResponse(url=exc.redirection, status_code=303)


app.include_router(auth.router)
app.include_router(utilisateurs.router)
app.include_router(parametres.router)
app.include_router(terrains.router, dependencies=[Depends(exiger_connexion)])
app.include_router(comptable.router, dependencies=[Depends(exiger_connexion)])
app.include_router(station.router, dependencies=[Depends(exiger_connexion)])
app.include_router(commercial.router, dependencies=[Depends(exiger_connexion)])


@app.get("/fichiers/{chemin_relatif:path}")
def servir_fichier_protege(chemin_relatif: str, utilisateur: models.Utilisateur = Depends(exiger_connexion)):
    """Sert les fichiers uploadés (photos, plans, factures) uniquement aux utilisateurs connectés."""
    base = UPLOADS_DIR.resolve()
    cible = (BASE_DIR / "static" / chemin_relatif).resolve()
    if base not in cible.parents or not cible.is_file():
        raise HTTPException(status_code=404, detail="Fichier introuvable")
    return FileResponse(cible)


# Appelé à l'import du module (pas seulement via un événement ASGI "startup") : sous
# Passenger/WSGI (hébergement OVH via passenger_wsgi.py + a2wsgi), le cycle de vie ASGI
# n'est pas nécessairement déclenché, alors que l'import du module, si.
def _initialiser() -> None:
    init_db()
    for chemin_sensible in (DB_PATH, CONFIG_PATH, GOOGLE_TOKEN_PATH):
        verrouiller_fichier(chemin_sensible)


_initialiser()


@app.get("/")
def dashboard(request: Request, db: Session = Depends(get_db), utilisateur: models.Utilisateur = Depends(exiger_connexion)):
    debut_mois = date.today().replace(day=1)

    nb_parcelles = db.query(func.count(models.Parcelle.id)).scalar() or 0
    total_achats_mois = (
        db.query(func.coalesce(func.sum(models.Achat.montant_total), 0))
        .filter(models.Achat.date_achat >= debut_mois)
        .scalar()
        or 0
    )
    total_ventes_mois = (
        db.query(func.coalesce(func.sum(models.VenteClient.montant), 0))
        .filter(models.VenteClient.date_vente >= debut_mois)
        .scalar()
        or 0
    )
    dernieres_entrees = (
        db.query(models.EntreeMarchandise)
        .order_by(models.EntreeMarchandise.date_entree.desc(), models.EntreeMarchandise.id.desc())
        .limit(5)
        .all()
    )
    dernieres_ventes = (
        db.query(models.VenteClient)
        .order_by(models.VenteClient.date_vente.desc(), models.VenteClient.id.desc())
        .limit(5)
        .all()
    )

    return templates.TemplateResponse(
        "dashboard.html",
        {
            "request": request,
            "nb_parcelles": nb_parcelles,
            "total_achats_mois": total_achats_mois,
            "total_ventes_mois": total_ventes_mois,
            "dernieres_entrees": dernieres_entrees,
            "dernieres_ventes": dernieres_ventes,
        },
    )
