from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any

from pif_compiler.classes.models import Ingredient, ToxIndicator
from pif_compiler.functions.db_utils import get_all_ingredienti, get_all_clienti, upsert_cliente, delete_cliente
from pif_compiler.functions.common_log import get_logger

logger = get_logger()
router = APIRouter()


class IngredientRequest(BaseModel):
    cas: str = Field(..., description="CAS number dell'ingrediente da cercare")
    force: bool = Field(default=False, description="Se True, ignora la cache e riesegue lo scraping")

    class Config:
        json_schema_extra = {
            "example": {
                "cas": "56-81-5",
                "force": False
            }
        }


class IngredientResponse(BaseModel):
    success: bool
    cas: str
    data: Optional[Dict[str, Any]] = None
    error: Optional[str] = None


@router.post("/ingredients/search", response_model=IngredientResponse, tags=["Ingredients"])
async def get_ingredient(request: IngredientRequest):
    """Recupera un ingrediente per CAS. Se esiste in cache lo restituisce, altrimenti lo crea da scraping."""
    logger.info(f"Richiesta ingrediente per CAS: {request.cas}, force refresh: {request.force}")

    try:
        ingredient = Ingredient.get_or_create(request.cas, force=request.force)

        if ingredient is None:
            logger.warning(f"Nessun dato trovato per CAS: {request.cas}")
            return IngredientResponse(
                success=False,
                cas=request.cas,
                error=f"Nessun dato trovato per CAS: {request.cas}"
            )

        logger.info(f"Ingrediente {request.cas} recuperato con successo")
        return IngredientResponse(
            success=True,
            cas=request.cas,
            data=ingredient.model_dump()
        )

    except Exception as e:
        logger.error(f"Errore recupero ingrediente {request.cas}: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Errore interno: {str(e)}"
        )


class AddToxIndicatorRequest(BaseModel):
    cas: str = Field(..., description="CAS number dell'ingrediente")
    indicator: str = Field(..., description="Tipo di indicatore (NOAEL, LOAEL, LD50)")
    value: int = Field(..., description="Valore dell'indicatore")
    unit: str = Field(..., description="Unità di misura (es. mg/kg bw/day)")
    route: str = Field(..., description="Via di esposizione (es. oral, dermal, inhalation)")
    toxicity_type: Optional[str] = Field(default=None, description="Tipo di tossicità (es. acute_toxicity, repeated_dose_toxicity)")
    ref: Optional[str] = Field(default=None, description="Riferimento o fonte del dato")

    class Config:
        json_schema_extra = {
            "example": {
                "cas": "56-81-5",
                "indicator": "NOAEL",
                "value": 1000,
                "unit": "mg/kg bw/day",
                "route": "oral",
                "toxicity_type": "repeated_dose_toxicity",
                "ref": "Custom - studio interno"
            }
        }


@router.post("/ingredients/add-tox-indicator", response_model=IngredientResponse, tags=["Ingredients"])
async def add_tox_indicator(request: AddToxIndicatorRequest):
    """Aggiunge un indicatore tossicologico custom a un ingrediente e ricalcola il best_case."""
    logger.info(f"Aggiunta indicatore tox custom per CAS: {request.cas}")

    try:
        ingredient = Ingredient.from_cas(request.cas)

        if ingredient is None:
            logger.warning(f"Ingrediente non trovato per CAS: {request.cas}")
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Ingrediente con CAS '{request.cas}' non trovato in cache"
            )

        new_indicator = ToxIndicator(
            indicator=request.indicator,
            value=request.value,
            unit=request.unit,
            route=request.route,
            toxicity_type=request.toxicity_type,
            ref=request.ref
        )

        ingredient.add_tox_indicator(new_indicator)

        logger.info(f"Indicatore tox aggiunto per CAS {request.cas}, best_case: {ingredient.toxicity.best_case.indicator if ingredient.toxicity.best_case else 'nessuno'}")
        return IngredientResponse(
            success=True,
            cas=request.cas,
            data=ingredient.model_dump()
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Errore aggiunta indicatore tox per {request.cas}: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Errore interno: {str(e)}"
        )


class IngredientListResponse(BaseModel):
    success: bool
    total: int
    data: Optional[List[Dict[str, Any]]] = None
    error: Optional[str] = None


@router.get("/ingredients/list", response_model=IngredientListResponse, tags=["Ingredients"])
async def list_ingredients():
    """Restituisce tutti gli ingredienti gia presenti nella tabella ingredienti di PostgreSQL."""
    logger.info("Recupero lista ingredienti dal DB")

    try:
        rows = get_all_ingredienti()

        ingredients = []
        for row in rows:
            id_, cas, mongo_id, dap, cosing, tox, created_at = row
            ingredients.append({
                "id": id_,
                "cas": cas,
                "mongo_id": mongo_id,
                "dap": dap,
                "cosing": cosing,
                "tox": tox,
                "created_at": created_at.isoformat() if created_at else None,
            })

        logger.info(f"Recuperati {len(ingredients)} ingredienti")
        return IngredientListResponse(
            success=True,
            total=len(ingredients),
            data=ingredients,
        )

    except Exception as e:
        logger.error(f"Errore recupero lista ingredienti: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Errore interno: {str(e)}"
        )


class ClientListResponse(BaseModel):
    success: bool
    data: Optional[List[Dict[str, Any]]] = None
    error: Optional[str] = None


class ClientCreateRequest(BaseModel):
    nome_cliente: str = Field(..., description="Nome del cliente")


class ClientCreateResponse(BaseModel):
    success: bool
    data: Optional[Dict[str, Any]] = None
    error: Optional[str] = None


@router.get("/ingredients/clients", response_model=ClientListResponse, tags=["Clients"])
async def list_clients():
    """Restituisce tutti i clienti registrati."""
    logger.info("Recupero lista clienti")

    try:
        rows = get_all_clienti()
        clients = [{"id_cliente": r[0], "nome_cliente": r[1]} for r in rows]

        logger.info(f"Recuperati {len(clients)} clienti")
        return ClientListResponse(success=True, data=clients)

    except Exception as e:
        logger.error(f"Errore recupero clienti: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Errore interno: {str(e)}"
        )


@router.delete("/ingredients/clients/{nome_cliente}", tags=["Clients"])
async def remove_client(nome_cliente: str):
    """Elimina un cliente per nome. Fallisce se ha ordini collegati."""
    result = delete_cliente(nome_cliente)
    if result is None:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Il cliente '{nome_cliente}' ha ordini collegati e non può essere eliminato"
        )
    if result is False:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Errore interno durante l'eliminazione"
        )
    if not result:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Cliente '{nome_cliente}' non trovato"
        )
    logger.info(f"Cliente '{nome_cliente}' eliminato")
    return {"success": True, "nome_cliente": nome_cliente}


@router.post("/ingredients/clients", response_model=ClientCreateResponse, tags=["Clients"])
async def create_client(request: ClientCreateRequest):
    """Crea o recupera un cliente. Ritorna id_cliente."""
    logger.info(f"Creazione/recupero cliente: {request.nome_cliente}")

    try:
        id_cliente = upsert_cliente(request.nome_cliente)

        if id_cliente is None:
            return ClientCreateResponse(
                success=False,
                error=f"Errore nel salvataggio del cliente '{request.nome_cliente}'"
            )

        logger.info(f"Cliente '{request.nome_cliente}' con id_cliente={id_cliente}")
        return ClientCreateResponse(
            success=True,
            data={"id_cliente": id_cliente, "nome_cliente": request.nome_cliente}
        )

    except Exception as e:
        logger.error(f"Errore creazione cliente: {str(e)}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Errore interno: {str(e)}"
        )
