from fastapi import APIRouter, Depends, HTTPException, Query, status from typing import List, Optional, Dict, Any import json from datetime import datetime from core.database import get_database, DatabaseManager from models.pietanze import PietanzaCreate, PietanzaUpdate, PietanzaResponse from models.common import ErrorResponse from core.auth import get_current_user, get_current_admin_user from core.exceptions import PietanzaNotFoundError, DatabaseError router = APIRouter(prefix="/pietanze", tags=["Pietanze"]) @router.get("/", response_model=List[PietanzaResponse]) async def list_pietanze( skip: int = Query(0, ge=0, description="Numero di elementi da saltare per la paginazione"), limit: int = Query(20, ge=1, le=100, description="Numero di elementi da restituire"), search: Optional[str] = Query(None, description="Ricerca in nome e descrizione"), allergeni: Optional[str] = Query(None, description="Filtra per allergeni (separati da virgola)"), db: DatabaseManager = Depends(get_database) ): """Ottieni lista delle pietanze disponibili con filtri opzionali""" try: # Costruisci query con filtri where_conditions = [] params = [] param_count = 0 if search: param_count += 1 where_conditions.append(f"(nome ILIKE ${param_count} OR descrizione ILIKE ${param_count})") params.append(f"%{search}%") if allergeni: allergen_list = [a.strip() for a in allergeni.split(",")] param_count += 1 where_conditions.append(f"allergeni ?| ${param_count}") params.append(allergen_list) where_clause = "" if where_conditions: where_clause = "WHERE " + " AND ".join(where_conditions) # Conta il totale degli elementi count_query = f"SELECT COUNT(*) FROM pietanze {where_clause}" count_result = await db.execute_one(count_query, *params) total = count_result[0] if count_result else 0 # Ottieni elementi con paginazione param_count += 1 limit_param = param_count param_count += 1 offset_param = param_count query = f""" SELECT id, nome, descrizione, allergeni, created_at, updated_at FROM pietanze {where_clause} ORDER BY nome LIMIT ${limit_param} OFFSET ${offset_param} """ params.extend([limit, skip]) rows = await db.execute_query(query, *params) pietanze = [] for row in rows: pietanze.append(PietanzaResponse( id=row['id'], nome=row['nome'], descrizione=row['descrizione'], allergeni=row['allergeni'] or [], created_at=row['created_at'], updated_at=row['updated_at'] )) return pietanze except Exception as e: raise DatabaseError(f"Errore nel recupero delle pietanze: {str(e)}") @router.get("/{pietanza_id}", response_model=PietanzaResponse) async def get_pietanza( pietanza_id: int, db: DatabaseManager = Depends(get_database) ): """Ottieni pietanza specifica per ID""" try: query = """ SELECT id, nome, descrizione, allergeni, created_at, updated_at FROM pietanze WHERE id = $1 """ row = await db.execute_one(query, pietanza_id) if not row: raise PietanzaNotFoundError(pietanza_id) return PietanzaResponse( id=row['id'], nome=row['nome'], descrizione=row['descrizione'], allergeni=row['allergeni'] or [], created_at=row['created_at'], updated_at=row['updated_at'] ) except PietanzaNotFoundError: raise except Exception as e: raise DatabaseError(f"Errore nel recupero della pietanza: {str(e)}") @router.post("/", response_model=PietanzaResponse, status_code=status.HTTP_201_CREATED) async def create_pietanza( pietanza: PietanzaCreate, current_user: Dict[str, Any] = Depends(get_current_admin_user), db: DatabaseManager = Depends(get_database) ): """Crea nuova pietanza (solo amministratori)""" try: query = """ INSERT INTO pietanze (nome, descrizione, allergeni, created_at, updated_at) VALUES ($1, $2, $3, $4, $4) RETURNING id, nome, descrizione, allergeni, created_at, updated_at """ now = datetime.utcnow() allergeni_json = json.dumps(pietanza.allergeni) if pietanza.allergeni else None row = await db.execute_one( query, pietanza.nome, pietanza.descrizione, allergeni_json, now ) if not row: raise DatabaseError("Errore nella creazione della pietanza") return PietanzaResponse( id=row['id'], nome=row['nome'], descrizione=row['descrizione'], allergeni=row['allergeni'] or [], created_at=row['created_at'], updated_at=row['updated_at'] ) except Exception as e: raise DatabaseError(f"Errore nella creazione della pietanza: {str(e)}") @router.put("/{pietanza_id}", response_model=PietanzaResponse) async def update_pietanza( pietanza_id: int, pietanza_update: PietanzaUpdate, current_user: Dict[str, Any] = Depends(get_current_admin_user), db: DatabaseManager = Depends(get_database) ): """Aggiorna pietanza esistente (solo amministratori)""" try: # Verifica se la pietanza esiste existing = await db.execute_one("SELECT id FROM pietanze WHERE id = $1", pietanza_id) if not existing: raise PietanzaNotFoundError(pietanza_id) # Costruisci query di aggiornamento dinamicamente update_fields = [] params = [] param_count = 0 if pietanza_update.nome is not None: param_count += 1 update_fields.append(f"nome = ${param_count}") params.append(pietanza_update.nome) if pietanza_update.descrizione is not None: param_count += 1 update_fields.append(f"descrizione = ${param_count}") params.append(pietanza_update.descrizione) if pietanza_update.allergeni is not None: param_count += 1 update_fields.append(f"allergeni = ${param_count}") params.append(json.dumps(pietanza_update.allergeni)) if not update_fields: # Nessun campo da aggiornare, restituisci pietanza corrente return await get_pietanza(pietanza_id, db) # Aggiungi updated_at e pietanza_id param_count += 1 update_fields.append(f"updated_at = ${param_count}") params.append(datetime.utcnow()) param_count += 1 params.append(pietanza_id) query = f""" UPDATE pietanze SET {', '.join(update_fields)} WHERE id = ${param_count} RETURNING id, nome, descrizione, allergeni, created_at, updated_at """ row = await db.execute_one(query, *params) return PietanzaResponse( id=row['id'], nome=row['nome'], descrizione=row['descrizione'], allergeni=row['allergeni'] or [], created_at=row['created_at'], updated_at=row['updated_at'] ) except PietanzaNotFoundError: raise except Exception as e: raise DatabaseError(f"Errore nell'aggiornamento della pietanza: {str(e)}") @router.delete("/{pietanza_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_pietanza( pietanza_id: int, current_user: Dict[str, Any] = Depends(get_current_admin_user), db: DatabaseManager = Depends(get_database) ): """Elimina pietanza (solo amministratori)""" try: # Verifica se la pietanza è associata a qualche pasto usando operatori JSONB # Controlla se l'ID della pietanza (come stringa) appare come chiave in qualsiasi portata pasto_check = await db.execute_one(""" SELECT COUNT(*) FROM pasti WHERE EXISTS ( SELECT 1 FROM jsonb_each(portate) AS p WHERE jsonb_typeof(p.value) = 'object' AND p.value ? $1 ) """, str(pietanza_id)) if pasto_check and pasto_check[0] > 0: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Impossibile eliminare la pietanza: è ancora associata a uno o più pasti" ) # Elimina la pietanza e verifica se esisteva result = await db.execute_one("DELETE FROM pietanze WHERE id = $1 RETURNING id", pietanza_id) if not result: raise PietanzaNotFoundError(pietanza_id) except PietanzaNotFoundError: raise except HTTPException: raise except Exception as e: raise DatabaseError(f"Errore nell'eliminazione della pietanza: {str(e)}")