"""API Produits — FastAPI avec CRUD en mémoire, pagination, Pydantic v2."""
from __future__ import annotations

from contextlib import asynccontextmanager
from datetime import datetime
from typing import AsyncGenerator

from fastapi import Depends, FastAPI, HTTPException, Query, status

from schemas import (
    CategoryEnum,
    PaginatedResponse,
    ProductCreate,
    ProductResponse,
    ProductUpdate,
)

# ── Stockage en mémoire ──────────────────────────────────────
produits_db: list[dict] = []
_next_id = 1


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """Initialise quelques produits de démonstration au démarrage."""
    global _next_id
    produits_db.extend([
        {"id": 1, "name": "Laptop Pro", "price": 1299.99, "stock": 5,
         "category": "electronique", "description": "Laptop haute performance",
         "created_at": datetime.now(), "updated_at": None},
        {"id": 2, "name": "T-Shirt Basic", "price": 19.99, "stock": 100,
         "category": "vetement", "description": None,
         "created_at": datetime.now(), "updated_at": None},
        {"id": 3, "name": "Pommes Bio", "price": 3.50, "stock": 50,
         "category": "alimentaire", "description": "Pommes de saison",
         "created_at": datetime.now(), "updated_at": None},
    ])
    _next_id = 4
    yield
    produits_db.clear()


app = FastAPI(
    title="API Produits",
    description="Gestion de produits avec FastAPI + Pydantic v2",
    version="1.0.0",
    lifespan=lifespan,
)


# ── Dépendance pagination ────────────────────────────────────
class PaginationParams:
    def __init__(
        self,
        page: int = Query(1, ge=1),
        limit: int = Query(10, ge=1, le=50),
    ) -> None:
        self.page = page
        self.limit = limit
        self.offset = (page - 1) * limit


# ── Routes ───────────────────────────────────────────────────

@app.get("/health")
async def health() -> dict:
    return {"status": "ok", "products_count": len(produits_db), "timestamp": datetime.now().isoformat()}


@app.get(
    "/produits",
    response_model=PaginatedResponse[ProductResponse],
    summary="Lister les produits",
    tags=["Produits"],
)
async def list_products(
    pagination: PaginationParams = Depends(),
    category: CategoryEnum | None = Query(None, description="Filtrer par catégorie"),
    q: str | None = Query(None, min_length=2, description="Recherche dans le nom"),
) -> PaginatedResponse[ProductResponse]:
    filtered = produits_db.copy()
    if category:
        filtered = [p for p in filtered if p["category"] == category.value]
    if q:
        filtered = [p for p in filtered if q.lower() in p["name"].lower()]

    total = len(filtered)
    items = filtered[pagination.offset : pagination.offset + pagination.limit]
    return PaginatedResponse[ProductResponse].create(
        items=[ProductResponse(**p) for p in items],
        total=total,
        page=pagination.page,
        limit=pagination.limit,
    )


@app.post(
    "/produits",
    response_model=ProductResponse,
    status_code=201,
    summary="Créer un produit",
    tags=["Produits"],
)
async def create_product(data: ProductCreate) -> ProductResponse:
    global _next_id
    produit = {
        "id": _next_id,
        **data.model_dump(),
        "created_at": datetime.now(),
        "updated_at": None,
    }
    produits_db.append(produit)
    _next_id += 1
    return ProductResponse(**produit)


@app.get(
    "/produits/{product_id}",
    response_model=ProductResponse,
    summary="Récupérer un produit",
    tags=["Produits"],
)
async def get_product(product_id: int) -> ProductResponse:
    produit = next((p for p in produits_db if p["id"] == product_id), None)
    if produit is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Produit {product_id} introuvable",
        )
    return ProductResponse(**produit)


@app.put(
    "/produits/{product_id}",
    response_model=ProductResponse,
    summary="Mettre à jour un produit",
    tags=["Produits"],
)
async def update_product(product_id: int, data: ProductUpdate) -> ProductResponse:
    idx = next((i for i, p in enumerate(produits_db) if p["id"] == product_id), None)
    if idx is None:
        raise HTTPException(status_code=404, detail=f"Produit {product_id} introuvable")

    updates = data.model_dump(exclude_unset=True)
    produits_db[idx].update({**updates, "updated_at": datetime.now()})
    return ProductResponse(**produits_db[idx])


@app.delete(
    "/produits/{product_id}",
    status_code=204,
    summary="Supprimer un produit",
    tags=["Produits"],
)
async def delete_product(product_id: int) -> None:
    global produits_db
    idx = next((i for i, p in enumerate(produits_db) if p["id"] == product_id), None)
    if idx is None:
        raise HTTPException(status_code=404, detail=f"Produit {product_id} introuvable")
    produits_db.pop(idx)
