from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import pandas as pd
import os
from typing import Optional

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

DATA_PATH = "/data/prenoms.csv"
df_cache = None
autocomplete_cache = None
alltime_cache = None   # total all-time par (prenom, sexe)

def get_df():
    global df_cache, autocomplete_cache
    if df_cache is None:
        if not os.path.exists(DATA_PATH):
            raise HTTPException(status_code=503, detail="Fichier prenoms.csv non trouvé dans /data/")
        df_cache = pd.read_csv(DATA_PATH, sep=";", dtype={"sexe": int, "prenom": str, "periode": int, "valeur": int})
        df_cache = df_cache[df_cache["prenom"] != "_PRENOMS_RARES"]
        df_cache["longueur"] = df_cache["prenom"].str.len()
        # Précalcul all-time total (utilisé par /api/top)
        global alltime_cache
        alltime_cache = df_cache.groupby(["prenom", "sexe"])["valeur"].sum().reset_index()
        alltime_cache.columns = ["prenom", "sexe", "total"]
        # Build autocomplete index: prenom -> total all-time, sexe majority
        agg = df_cache.groupby(["prenom", "sexe"])["valeur"].sum().reset_index()
        totals = agg.groupby("prenom")["valeur"].sum().reset_index()
        totals.columns = ["prenom", "total"]
        # dominant sexe
        dom = agg.loc[agg.groupby("prenom")["valeur"].idxmax(), ["prenom","sexe"]]
        merged = totals.merge(dom, on="prenom")
        autocomplete_cache = merged.sort_values("total", ascending=False).reset_index(drop=True)
    return df_cache

def get_autocomplete():
    get_df()
    return autocomplete_cache

@app.get("/api/status")
def status():
    if os.path.exists(DATA_PATH):
        try:
            df = get_df()
            return {"ok": True, "rows": len(df), "prenoms": df["prenom"].nunique()}
        except Exception as e:
            return {"ok": False, "error": str(e)}
    return {"ok": False, "error": "Fichier manquant"}

@app.get("/api/autocomplete")
def autocomplete(q: str, limit: int = 8):
    ac = get_autocomplete()
    prefix = q.upper().strip()
    if not prefix:
        return []
    matches = ac[ac["prenom"].str.startswith(prefix)].head(limit)
    return matches.to_dict(orient="records")

@app.get("/api/evolution/{prenom}")
def evolution(prenom: str):
    df = get_df()
    prenom = prenom.upper()
    sub = df[df["prenom"] == prenom].groupby(["periode", "sexe"])["valeur"].sum().reset_index()
    if sub.empty:
        raise HTTPException(status_code=404, detail=f"Prénom '{prenom}' introuvable")
    return sub.to_dict(orient="records")

@app.get("/api/compare")
def compare(a: str, b: str):
    df = get_df()
    result = {}
    for prenom in [a.upper(), b.upper()]:
        sub = df[df["prenom"] == prenom].groupby(["periode", "sexe"])["valeur"].sum().reset_index()
        result[prenom] = sub.to_dict(orient="records")
    return result

@app.get("/api/heatmap/{prenom}")
def heatmap(prenom: str):
    """Returns total births per decade per sexe"""
    df = get_df()
    prenom = prenom.upper()
    sub = df[df["prenom"] == prenom].copy()
    if sub.empty:
        raise HTTPException(status_code=404, detail=f"Prénom '{prenom}' introuvable")
    sub["decade"] = (sub["periode"] // 10) * 10
    agg = sub.groupby(["decade", "sexe"])["valeur"].sum().reset_index()
    return agg.to_dict(orient="records")

@app.get("/api/search")
def search(
    q: Optional[str] = None,
    sexe: Optional[int] = None,
    longueur_min: int = 1,
    longueur_max: int = 20,
    popularite_min: int = 0,
    popularite_max: int = 10000000,
    peak_min: Optional[int] = None,
    peak_max: Optional[int] = None,
    periode_min: int = 1900,
    periode_max: int = 2024,
    limit: int = 50
):
    df = get_df()

    # Total all-time (pour popularité et affichage)
    agg_all = df.groupby(["prenom", "sexe", "longueur"])["valeur"].sum().reset_index()
    agg_all.columns = ["prenom", "sexe", "longueur", "total"]

    # Total sur la période choisie (pour filtrer les prénoms actifs sur cette période)
    sub = df[(df["periode"] >= periode_min) & (df["periode"] <= periode_max)]
    agg_period = sub.groupby(["prenom", "sexe"])["valeur"].sum().reset_index()
    agg_period.columns = ["prenom", "sexe", "total_periode"]

    # Garder seulement les prénoms présents sur la période (au moins 1 naissance)
    agg = agg_all.merge(agg_period, on=["prenom", "sexe"], how="inner")

    if sexe is not None:
        agg = agg[agg["sexe"] == sexe]
    agg = agg[(agg["longueur"] >= longueur_min) & (agg["longueur"] <= longueur_max)]
    if q:
        agg = agg[agg["prenom"].str.startswith(q.upper())]

    # Popularité filtrée sur total all-time
    agg = agg[(agg["total"] >= popularite_min) & (agg["total"] <= popularite_max)]

    agg = agg.sort_values("total_periode", ascending=False).head(limit)
    return agg.to_dict(orient="records")

@app.get("/api/top")
def top(
    sexe: Optional[int] = None,
    periode_min: int = 1900,
    periode_max: int = 2024,
    order: str = "desc",
    page: int = 1,
    per_page: int = 50
):
    df = get_df()  # assure que alltime_cache est initialisé
    # Total sur la période (pour tri)
    sub = df[(df["periode"] >= periode_min) & (df["periode"] <= periode_max)]
    agg_period = sub.groupby(["prenom", "sexe"])["valeur"].sum().reset_index()
    agg_period.columns = ["prenom", "sexe", "total_periode"]
    # Joindre avec all-time (précalculé)
    agg = alltime_cache.merge(agg_period, on=["prenom","sexe"], how="inner")
    if sexe is not None:
        agg = agg[agg["sexe"] == sexe]
    agg = agg.sort_values("total_periode", ascending=(order=="asc"))
    total_count = len(agg)
    offset = (page - 1) * per_page
    page_data = agg.iloc[offset:offset+per_page].copy()
    return {"total": total_count, "page": page, "per_page": per_page, "data": page_data.to_dict(orient="records")}

@app.get("/api/stats")
def stats():
    df = get_df()
    return {
        "total_naissances": int(df["valeur"].sum()),
        "nb_prenoms": int(df["prenom"].nunique()),
        "periode_min": int(df["periode"].min()),
        "periode_max": int(df["periode"].max()),
    }
