"""
Cave à Vin — Application de gestion d'inventaire
Backend FastAPI + SQLite, 100 % local.
"""
import csv
import io
import shutil
from contextlib import asynccontextmanager
from datetime import date, datetime
from pathlib import Path
from typing import Optional

from fastapi import Depends, FastAPI, HTTPException, Query
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from sqlmodel import Session, or_, select

from database import engine, get_session, init_db, migrate_db, DB_FILE
from models import Bouteille, BouteilleCreate, BouteilleUpdate, Cave, CaveCreate, CaveUpdate
import lwin_db


# ── Démarrage de l'application ────────────────────────────────────────────────

@asynccontextmanager
async def lifespan(app: FastAPI):
    init_db()
    migrate_db()
    _seed_demo()
    lwin_db.init_lwin(background=True)
    yield


app = FastAPI(title="Cave à Vin", version="2.0", lifespan=lifespan)


def _seed_demo():
    """Insère des bouteilles de démonstration si la cave est vide."""
    with Session(engine) as session:
        if session.exec(select(Bouteille)).first():
            return  # cave déjà peuplée

        # Crée une cave de démonstration
        cave = Cave(nom="Cave principale", type="rayonnage", nb_etageres=5,
                    nb_positions=12, temperature=12.0, description="Cave de garde — sous-sol")
        session.add(cave)
        session.commit()
        session.refresh(cave)

        demo = [
            Bouteille(
                nom="Château Margaux",
                producteur="Château Margaux",
                appellation="Margaux AOC",
                region="Bordeaux",
                couleur="rouge",
                cepages="Cabernet Sauvignon, Merlot",
                millesime=2015,
                quantite=6,
                prix_achat=320.0,
                date_achat=date(2022, 3, 15),
                apogee_debut=2025,
                apogee_fin=2045,
                note=19.0,
                commentaire="Exceptionnel, grand potentiel de garde.",
            ),
            Bouteille(
                nom="Puligny-Montrachet Les Combettes",
                producteur="Domaine Leflaive",
                appellation="Puligny-Montrachet 1er Cru",
                region="Bourgogne",
                couleur="blanc",
                cepages="Chardonnay",
                millesime=2019,
                quantite=3,
                prix_achat=95.0,
                date_achat=date(2023, 1, 10),
                apogee_debut=2024,
                apogee_fin=2029,
                note=17.5,
                commentaire="Minéralité remarquable, belle longueur.",
            ),
            Bouteille(
                nom="Châteauneuf-du-Pape",
                producteur="Château Rayas",
                appellation="Châteauneuf-du-Pape AOC",
                region="Vallée du Rhône",
                couleur="rouge",
                cepages="Grenache",
                millesime=2018,
                quantite=4,
                prix_achat=180.0,
                date_achat=date(2022, 6, 20),
                apogee_debut=2023,
                apogee_fin=2028,
                note=18.0,
                commentaire="Complexe, épices et fruits rouges mûrs.",
            ),
            Bouteille(
                nom="Grande Cuvée",
                producteur="Krug",
                appellation="Champagne AOC",
                region="Champagne",
                couleur="effervescent",
                cepages="Pinot Noir, Chardonnay, Meunier",
                millesime=None,
                quantite=2,
                prix_achat=210.0,
                date_achat=date(2023, 12, 1),
                apogee_fin=2030,
                note=19.5,
                commentaire="Bulle fine, brioche, noix. Pour les grandes occasions.",
            ),
            Bouteille(
                nom="Sancerre Blanc",
                producteur="Henri Bourgeois",
                appellation="Sancerre AOC",
                region="Loire",
                couleur="blanc",
                cepages="Sauvignon Blanc",
                millesime=2022,
                quantite=6,
                prix_achat=22.0,
                date_achat=date(2023, 9, 5),
                apogee_debut=2023,
                apogee_fin=2026,
                note=15.5,
                commentaire="Frais, agrumes. À boire rapidement.",
            ),
            Bouteille(
                nom="Bandol Rosé",
                producteur="Domaine Tempier",
                appellation="Bandol AOC",
                region="Provence",
                couleur="rosé",
                cepages="Mourvèdre, Grenache, Cinsault",
                millesime=2022,
                quantite=3,
                prix_achat=35.0,
                date_achat=date(2023, 7, 14),
                apogee_debut=2023,
                apogee_fin=2025,
                note=16.0,
                commentaire="Le meilleur rosé de Provence.",
            ),
            # Un emplacement précis n'est possible que pour une quantité de 1
            # bouteille (1 emplacement = 1 bouteille physique) — ces deux entrées
            # illustrent le plan de cave avec un vrai emplacement occupé.
            Bouteille(
                nom="Château Margaux",
                producteur="Château Margaux",
                appellation="Margaux AOC",
                region="Bordeaux",
                couleur="rouge",
                cepages="Cabernet Sauvignon, Merlot",
                millesime=2015,
                quantite=1,
                cave_id=cave.id, etagere=1, position=1,
                prix_achat=320.0,
                date_achat=date(2022, 3, 15),
                apogee_debut=2025,
                apogee_fin=2045,
                note=19.0,
                commentaire="Exceptionnel, grand potentiel de garde.",
            ),
            Bouteille(
                nom="Grande Cuvée",
                producteur="Krug",
                appellation="Champagne AOC",
                region="Champagne",
                couleur="effervescent",
                cepages="Pinot Noir, Chardonnay, Meunier",
                millesime=None,
                quantite=1,
                cave_id=cave.id, etagere=3, position=1,
                prix_achat=210.0,
                date_achat=date(2023, 12, 1),
                apogee_fin=2030,
                note=19.5,
                commentaire="Bulle fine, brioche, noix. Pour les grandes occasions.",
            ),
        ]
        for b in demo:
            session.add(b)
        session.commit()


# ── Caves (stockage) ──────────────────────────────────────────────────────────

@app.get("/api/caves")
def lister_caves(session: Session = Depends(get_session)):
    return session.exec(select(Cave)).all()


@app.post("/api/caves", status_code=201)
def creer_cave(data: CaveCreate, session: Session = Depends(get_session)):
    cave = Cave(**data.model_dump())
    session.add(cave)
    session.commit()
    session.refresh(cave)
    return cave


@app.put("/api/caves/{id}")
def modifier_cave(id: int, data: CaveUpdate, session: Session = Depends(get_session)):
    cave = session.get(Cave, id)
    if not cave:
        raise HTTPException(404, "Cave introuvable")
    for k, v in data.model_dump(exclude_unset=True).items():
        setattr(cave, k, v)
    session.add(cave)
    session.commit()
    session.refresh(cave)
    return cave


@app.delete("/api/caves/{id}", status_code=204)
def supprimer_cave(id: int, session: Session = Depends(get_session)):
    cave = session.get(Cave, id)
    if not cave:
        raise HTTPException(404, "Cave introuvable")
    # Détache les bouteilles avant suppression
    bouteilles = session.exec(select(Bouteille).where(Bouteille.cave_id == id)).all()
    for b in bouteilles:
        b.cave_id = None
        b.etagere = None
        b.position = None
        session.add(b)
    session.delete(cave)
    session.commit()


@app.get("/api/caves/{id}/plan")
def plan_cave(id: int, session: Session = Depends(get_session)):
    """Retourne la grille d'occupation d'une cave."""
    cave = session.get(Cave, id)
    if not cave:
        raise HTTPException(404, "Cave introuvable")

    bouteilles = session.exec(
        select(Bouteille)
        .where(Bouteille.cave_id == id)
        .where(Bouteille.quantite > 0)
    ).all()

    grille = {}
    for b in bouteilles:
        if b.etagere is not None and b.position is not None:
            key = f"{b.etagere}-{b.position}"
            grille[key] = {
                "id": b.id,
                "nom": b.nom,
                "couleur": b.couleur,
                "millesime": b.millesime,
                "quantite": b.quantite,
                "producteur": b.producteur,
            }

    return {"cave": cave, "grille": grille}


# ── Recherche externe (Open Food Facts) ──────────────────────────────────────

@app.get("/api/search-vin")
async def rechercher_vin(q: str = Query(..., min_length=2)):
    """Recherche dans la base LWIN locale (185 000 vins)."""
    if not lwin_db.is_ready():
        raise HTTPException(503, detail="Base LWIN en cours de chargement — réessayez dans quelques secondes")
    resultats = lwin_db.search(q, limit=20)
    return resultats


# ── Routes API bouteilles ─────────────────────────────────────────────────────

@app.get("/api/bouteilles")
def lister_bouteilles(
    q: Optional[str] = Query(None, description="Recherche plein texte"),
    couleur: Optional[str] = Query(None),
    region: Optional[str] = Query(None),
    appellation: Optional[str] = Query(None),
    millesime: Optional[int] = Query(None),
    sort: str = Query("nom"),
    order: str = Query("asc"),
    session: Session = Depends(get_session),
):
    query = select(Bouteille)

    if q:
        query = query.where(
            or_(
                Bouteille.nom.ilike(f"%{q}%"),
                Bouteille.producteur.ilike(f"%{q}%"),
                Bouteille.appellation.ilike(f"%{q}%"),
                Bouteille.region.ilike(f"%{q}%"),
                Bouteille.cepages.ilike(f"%{q}%"),
                Bouteille.commentaire.ilike(f"%{q}%"),
            )
        )
    if couleur:
        query = query.where(Bouteille.couleur == couleur)
    if region:
        query = query.where(Bouteille.region.ilike(f"%{region}%"))
    if appellation:
        query = query.where(Bouteille.appellation.ilike(f"%{appellation}%"))
    if millesime:
        query = query.where(Bouteille.millesime == millesime)

    colonnes_tri = {
        "nom": Bouteille.nom,
        "region": Bouteille.region,
        "couleur": Bouteille.couleur,
        "millesime": Bouteille.millesime,
        "apogee_fin": Bouteille.apogee_fin,
        "quantite": Bouteille.quantite,
        "prix_achat": Bouteille.prix_achat,
        "note": Bouteille.note,
    }
    col = colonnes_tri.get(sort, Bouteille.nom)
    query = query.order_by(col.desc() if order == "desc" else col.asc())

    return session.exec(query).all()


@app.post("/api/bouteilles", status_code=201)
def creer_bouteille(bouteille: BouteilleCreate, session: Session = Depends(get_session)):
    if bouteille.etagere is not None and bouteille.position is not None and bouteille.quantite != 1:
        raise HTTPException(
            400,
            detail="Un emplacement précis ne peut être assigné qu'à une quantité de 1 bouteille "
                   "(1 emplacement = 1 bouteille). Réduisez la quantité à 1 ou laissez l'emplacement vide.",
        )
    if bouteille.cave_id and bouteille.etagere is not None and bouteille.position is not None:
        conflit = session.exec(
            select(Bouteille).where(
                Bouteille.cave_id == bouteille.cave_id,
                Bouteille.etagere == bouteille.etagere,
                Bouteille.position == bouteille.position,
            )
        ).first()
        if conflit:
            raise HTTPException(409, detail=f"L'emplacement Étagère {bouteille.etagere} · Position {bouteille.position} est déjà occupé par « {conflit.nom} »")
    db = Bouteille(**bouteille.model_dump())
    session.add(db)
    session.commit()
    session.refresh(db)
    return db


@app.get("/api/bouteilles/{id}")
def lire_bouteille(id: int, session: Session = Depends(get_session)):
    b = session.get(Bouteille, id)
    if not b:
        raise HTTPException(status_code=404, detail="Bouteille introuvable")
    return b


@app.put("/api/bouteilles/{id}")
def modifier_bouteille(id: int, data: BouteilleUpdate, session: Session = Depends(get_session)):
    b = session.get(Bouteille, id)
    if not b:
        raise HTTPException(status_code=404, detail="Bouteille introuvable")

    maj = data.model_dump(exclude_unset=True)

    cave_id  = maj.get("cave_id",  b.cave_id)
    etagere  = maj.get("etagere",  b.etagere)
    position = maj.get("position", b.position)
    quantite = maj.get("quantite", b.quantite)
    if etagere is not None and position is not None and quantite != 1:
        raise HTTPException(
            400,
            detail="Un emplacement précis ne peut être assigné qu'à une quantité de 1 bouteille "
                   "(1 emplacement = 1 bouteille). Réduisez la quantité à 1 ou laissez l'emplacement vide.",
        )
    if cave_id and etagere is not None and position is not None:
        conflit = session.exec(
            select(Bouteille).where(
                Bouteille.cave_id == cave_id,
                Bouteille.etagere == etagere,
                Bouteille.position == position,
                Bouteille.id != id,
            )
        ).first()
        if conflit:
            raise HTTPException(409, detail=f"L'emplacement Étagère {etagere} · Position {position} est déjà occupé par « {conflit.nom} »")
    maj["date_modification"] = datetime.utcnow()
    for champ, valeur in maj.items():
        setattr(b, champ, valeur)

    session.add(b)
    session.commit()
    session.refresh(b)
    return b


@app.delete("/api/bouteilles/{id}", status_code=204)
def supprimer_bouteille(id: int, session: Session = Depends(get_session)):
    b = session.get(Bouteille, id)
    if not b:
        raise HTTPException(status_code=404, detail="Bouteille introuvable")
    session.delete(b)
    session.commit()


@app.post("/api/bouteilles/{id}/boire")
def boire_bouteille(id: int, session: Session = Depends(get_session)):
    b = session.get(Bouteille, id)
    if not b:
        raise HTTPException(status_code=404, detail="Bouteille introuvable")
    if b.quantite <= 0:
        raise HTTPException(status_code=400, detail="Stock déjà à zéro")
    b.quantite -= 1
    b.date_modification = datetime.utcnow()
    session.add(b)
    session.commit()
    session.refresh(b)
    return b


# ── Tableau de bord ───────────────────────────────────────────────────────────

@app.get("/api/dashboard")
def tableau_de_bord(session: Session = Depends(get_session)):
    bouteilles = session.exec(select(Bouteille)).all()

    total_bouteilles = sum(b.quantite for b in bouteilles)
    valeur_totale = sum((b.prix_achat or 0) * b.quantite for b in bouteilles)

    par_couleur: dict = {}
    par_region: dict = {}
    for b in bouteilles:
        par_couleur[b.couleur] = par_couleur.get(b.couleur, 0) + b.quantite
        reg = b.region or "Non précisée"
        par_region[reg] = par_region.get(reg, 0) + b.quantite

    return {
        "total_bouteilles": total_bouteilles,
        "valeur_totale": round(valeur_totale, 2),
        "nb_references": len(bouteilles),
        "par_couleur": par_couleur,
        "par_region": par_region,
    }


@app.get("/api/alertes")
def alertes_apogee(
    seuil: int = Query(2),
    session: Session = Depends(get_session),
):
    limite = datetime.now().year + seuil
    bouteilles = session.exec(
        select(Bouteille)
        .where(Bouteille.apogee_fin.is_not(None))
        .where(Bouteille.apogee_fin <= limite)
        .where(Bouteille.quantite > 0)
        .order_by(Bouteille.apogee_fin.asc())
    ).all()
    return bouteilles


# ── Statistiques détaillées ───────────────────────────────────────────────────

@app.get("/api/stats")
def statistiques(session: Session = Depends(get_session)):
    bouteilles = session.exec(select(Bouteille)).all()

    def repartition(attr: str) -> dict:
        d: dict = {}
        for b in bouteilles:
            v = getattr(b, attr) or "Non précisé"
            d[v] = d.get(v, 0) + b.quantite
        return d

    def top(d: dict, n: int = 10) -> list:
        return sorted(d.items(), key=lambda x: x[1], reverse=True)[:n]

    tranches_notes = {
        "< 14": 0, "14–15.5": 0, "16–17.5": 0, "18–19": 0, "19.5–20": 0, "Sans note": 0,
    }
    for b in bouteilles:
        if b.note is None:
            tranches_notes["Sans note"] += b.quantite
        elif b.note < 14:
            tranches_notes["< 14"] += b.quantite
        elif b.note <= 15.5:
            tranches_notes["14–15.5"] += b.quantite
        elif b.note <= 17.5:
            tranches_notes["16–17.5"] += b.quantite
        elif b.note <= 19:
            tranches_notes["18–19"] += b.quantite
        else:
            tranches_notes["19.5–20"] += b.quantite

    decades: dict = {}
    sans_annee = 0
    for b in bouteilles:
        if b.millesime is None:
            sans_annee += b.quantite
        else:
            dec = f"{(b.millesime // 10) * 10}s"
            decades[dec] = decades.get(dec, 0) + b.quantite
    if sans_annee:
        decades["Sans année"] = sans_annee

    valeur_region: dict = {}
    valeur_couleur: dict = {}
    for b in bouteilles:
        if b.prix_achat:
            val = b.prix_achat * b.quantite
            reg = b.region or "Non précisée"
            valeur_region[reg] = round(valeur_region.get(reg, 0) + val, 2)
            valeur_couleur[b.couleur] = round(valeur_couleur.get(b.couleur, 0) + val, 2)

    par_appellation = repartition("appellation")
    par_producteur  = repartition("producteur")

    # Hiérarchie Pays > Région > Couleur (pour le sunburst)
    COULEUR_LABEL = {
        "rouge": "Rouge", "blanc": "Blanc", "rosé": "Rosé",
        "effervescent": "Effervescent", "autre": "Autre",
    }
    hierarchie: dict = {"name": "Cave", "children": []}
    par_pays_node: dict = {}
    for b in bouteilles:
        pays = b.pays or "Non précisé"
        region = b.region or "Non précisée"
        couleur = COULEUR_LABEL.get(b.couleur, b.couleur or "Autre")

        pays_node = par_pays_node.setdefault(pays, {"name": pays, "children": [], "_regions": {}})
        region_node = pays_node["_regions"].setdefault(
            region, {"name": region, "children": [], "_couleurs": {}}
        )
        couleur_node = region_node["_couleurs"].setdefault(
            couleur, {"name": couleur, "value": 0}
        )
        couleur_node["value"] += b.quantite

    for pays_node in par_pays_node.values():
        for region_node in pays_node["_regions"].values():
            region_node["children"] = sorted(
                region_node["_couleurs"].values(), key=lambda x: -x["value"]
            )
            del region_node["_couleurs"]
        pays_node["children"] = sorted(
            pays_node["_regions"].values(), key=lambda x: -sum(c["value"] for c in x["children"])
        )
        del pays_node["_regions"]
    hierarchie["children"] = sorted(
        par_pays_node.values(),
        key=lambda x: -sum(c["value"] for r in x["children"] for c in r["children"]),
    )

    # Répartition des apogées (pour le pie chart)
    annee = date.today().year
    apogees: dict = {}

    def ajouter_apogee(label: str, qte: int):
        apogees[label] = apogees.get(label, 0) + qte

    for b in bouteilles:
        if b.apogee_debut is None and b.apogee_fin is None:
            ajouter_apogee("Sans info", b.quantite)
            continue
        fin = b.apogee_fin if b.apogee_fin is not None else b.apogee_debut
        debut = b.apogee_debut if b.apogee_debut is not None else b.apogee_fin
        if fin < annee:
            ajouter_apogee("Apogée dépassée", b.quantite)
        elif debut <= annee <= fin:
            ajouter_apogee("À boire maintenant", b.quantite)
        else:
            delta = debut - annee
            if delta <= 2:
                ajouter_apogee("Dans 1–2 ans", b.quantite)
            elif delta <= 5:
                ajouter_apogee("Dans 3–5 ans", b.quantite)
            elif delta <= 10:
                ajouter_apogee("Dans 6–10 ans", b.quantite)
            else:
                ajouter_apogee("Plus de 10 ans", b.quantite)

    return {
        "par_couleur":     repartition("couleur"),
        "par_region":      repartition("region"),
        "par_pays":        repartition("pays"),
        "par_format":      repartition("format"),
        "top_appellations":top(par_appellation),
        "top_producteurs": top(par_producteur),
        "notes":           tranches_notes,
        "millesimes":      dict(sorted(decades.items())),
        "valeur_region":   dict(sorted(valeur_region.items(), key=lambda x: x[1], reverse=True)),
        "valeur_couleur":  valeur_couleur,
        "hierarchie":      hierarchie,
        "apogees":         apogees,
    }


# ── Export CSV ────────────────────────────────────────────────────────────────

@app.get("/api/export/csv")
def exporter_csv(session: Session = Depends(get_session)):
    bouteilles = session.exec(select(Bouteille)).all()

    buf = io.StringIO()
    writer = csv.writer(buf)
    writer.writerow([
        "id", "nom", "producteur", "appellation", "region", "pays",
        "couleur", "cepages", "millesime", "quantite", "format",
        "cave_id", "etagere", "position",
        "prix_achat", "date_achat", "apogee_debut",
        "apogee_fin", "note", "commentaire", "date_creation",
    ])
    for b in bouteilles:
        writer.writerow([
            b.id, b.nom, b.producteur, b.appellation, b.region, b.pays,
            b.couleur, b.cepages, b.millesime, b.quantite, b.format,
            b.cave_id, b.etagere, b.position,
            b.prix_achat, b.date_achat, b.apogee_debut,
            b.apogee_fin, b.note, b.commentaire, b.date_creation,
        ])

    buf.seek(0)
    return StreamingResponse(
        iter([buf.getvalue()]),
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=cave_a_vin.csv"},
    )


# ── Export texte ──────────────────────────────────────────────────────────────

@app.get("/api/export/txt")
def exporter_txt(session: Session = Depends(get_session)):
    bouteilles = session.exec(select(Bouteille).order_by(Bouteille.region, Bouteille.nom)).all()
    total = sum(b.quantite for b in bouteilles)
    valeur = sum((b.prix_achat or 0) * b.quantite for b in bouteilles)
    maintenant = datetime.now().strftime("%d/%m/%Y à %H:%M")

    lignes = [
        "╔══════════════════════════════════════════════════════════╗",
        "║              CAVE À VIN — Inventaire complet             ║",
        "╚══════════════════════════════════════════════════════════╝",
        f"  Exporté le : {maintenant}",
        f"  Total      : {total} bouteille{'s' if total > 1 else ''} ({len(bouteilles)} référence{'s' if len(bouteilles) > 1 else ''})",
        f"  Valeur est.: {valeur:,.2f} €".replace(",", " "),
        "",
    ]

    region_courante = None
    for b in bouteilles:
        reg = b.region or "Région non précisée"
        if reg != region_courante:
            region_courante = reg
            lignes += ["", f"━━━  {reg.upper()}  ━━━", ""]

        millesime = str(b.millesime) if b.millesime else "Sans année"
        lignes.append(f"  {b.nom}  [{millesime}]")
        if b.producteur:
            lignes.append(f"    Producteur  : {b.producteur}")
        if b.appellation:
            lignes.append(f"    Appellation : {b.appellation}")
        lignes.append(f"    Couleur     : {b.couleur.capitalize()}")
        if b.cepages:
            lignes.append(f"    Cépages     : {b.cepages}")
        lignes.append(f"    Quantité    : {b.quantite} bouteille{'s' if b.quantite > 1 else ''}")
        if b.format and b.format != "Bouteille 75cl":
            lignes.append(f"    Format      : {b.format}")
        if b.etagere is not None and b.position is not None:
            lignes.append(f"    Stockage    : Étagère {b.etagere}, position {b.position}")
        if b.prix_achat is not None:
            lignes.append(f"    Prix achat  : {b.prix_achat:.2f} €")
        if b.date_achat:
            lignes.append(f"    Date achat  : {b.date_achat}")
        if b.apogee_debut or b.apogee_fin:
            ap = f"{b.apogee_debut or '?'} – {b.apogee_fin or '?'}"
            lignes.append(f"    Apogée      : {ap}")
        if b.note is not None:
            lignes.append(f"    Note        : {b.note}/20")
        if b.commentaire:
            lignes.append(f"    Commentaire : {b.commentaire}")
        lignes.append("")

    contenu = "\n".join(lignes)
    return StreamingResponse(
        iter([contenu]),
        media_type="text/plain; charset=utf-8",
        headers={"Content-Disposition": "attachment; filename=cave_a_vin.txt"},
    )


# ── Sauvegarde ────────────────────────────────────────────────────────────────

@app.post("/api/backup")
def sauvegarder_base():
    src = DB_FILE
    if not src.exists():
        raise HTTPException(status_code=404, detail="cave.db introuvable")
    Path("backups").mkdir(exist_ok=True)
    horodatage = datetime.now().strftime("%Y%m%d_%H%M%S")
    dest = Path("backups") / f"cave_{horodatage}.db"
    shutil.copy2(src, dest)
    return {"message": f"Sauvegarde créée : {dest}", "fichier": str(dest)}


# ── Frontend statique ─────────────────────────────────────────────────────────

app.mount("/static", StaticFiles(directory="static"), name="static")


@app.get("/")
def index():
    return FileResponse("static/index.html")
