from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.api.deps import AuthSession, require_client_module_access
from app.db.session import get_db
from app.models.mix import Mix, MixIngredient
from app.models.raw_material import RawMaterial
from app.schemas.mix import MixCreate, MixIngredientCreate, MixIngredientUpdate, MixRead, MixUpdate
from app.services.costing_engine import calculate_mix_cost

router = APIRouter(prefix="/api/mixes", tags=["mixes"])


@router.get("", response_model=list[MixRead])
def list_mixes(
    limit: int = Query(default=100, ge=1, le=200),
    session: AuthSession = Depends(require_client_module_access("mix_master")),
    db: Session = Depends(get_db),
):
    mixes = db.scalars(select(Mix).where(Mix.tenant_id == session.tenant_id).order_by(Mix.name).limit(limit)).all()
    return [calculate_mix_cost(db, mix.id) for mix in mixes]


@router.post("", response_model=MixRead, status_code=status.HTTP_201_CREATED)
def create_mix(payload: MixCreate, session: AuthSession = Depends(require_client_module_access("mix_master", "edit")), db: Session = Depends(get_db)):
    mix = Mix(
        tenant_id=session.tenant_id,
        client_name=payload.client_name,
        name=payload.name,
        status=payload.status,
        version=payload.version,
        notes=payload.notes,
    )
    db.add(mix)
    db.flush()
    for ingredient in payload.ingredients:
        if db.scalar(
            select(RawMaterial).where(
                RawMaterial.id == ingredient.raw_material_id,
                RawMaterial.tenant_id == session.tenant_id,
            )
        ) is None:
            raise HTTPException(status_code=404, detail=f"Raw material {ingredient.raw_material_id} not found")
        db.add(
            MixIngredient(
                tenant_id=session.tenant_id,
                mix_id=mix.id,
                raw_material_id=ingredient.raw_material_id,
                quantity_kg=ingredient.quantity_kg,
                notes=ingredient.notes,
            )
        )
    db.commit()
    return calculate_mix_cost(db, mix.id)


@router.get("/{mix_id}", response_model=MixRead)
def get_mix(mix_id: int, session: AuthSession = Depends(require_client_module_access("mix_master")), db: Session = Depends(get_db)):
    if db.scalar(select(Mix.id).where(Mix.id == mix_id, Mix.tenant_id == session.tenant_id)) is None:
        raise HTTPException(status_code=404, detail="Mix not found")
    return calculate_mix_cost(db, mix_id)


@router.patch("/{mix_id}", response_model=MixRead)
def update_mix(mix_id: int, payload: MixUpdate, session: AuthSession = Depends(require_client_module_access("mix_master", "edit")), db: Session = Depends(get_db)):
    mix = db.scalar(select(Mix).where(Mix.id == mix_id, Mix.tenant_id == session.tenant_id))
    if mix is None:
        raise HTTPException(status_code=404, detail="Mix not found")
    for field, value in payload.model_dump(exclude_unset=True).items():
        setattr(mix, field, value)
    db.commit()
    return calculate_mix_cost(db, mix_id)


@router.post("/{mix_id}/ingredients", response_model=MixRead, status_code=status.HTTP_201_CREATED)
def add_mix_ingredient(mix_id: int, payload: MixIngredientCreate, session: AuthSession = Depends(require_client_module_access("mix_master", "edit")), db: Session = Depends(get_db)):
    if db.scalar(select(Mix.id).where(Mix.id == mix_id, Mix.tenant_id == session.tenant_id)) is None:
        raise HTTPException(status_code=404, detail="Mix not found")
    if db.scalar(select(RawMaterial.id).where(RawMaterial.id == payload.raw_material_id, RawMaterial.tenant_id == session.tenant_id)) is None:
        raise HTTPException(status_code=404, detail="Raw material not found")
    db.add(
        MixIngredient(
            tenant_id=session.tenant_id,
            mix_id=mix_id,
            raw_material_id=payload.raw_material_id,
            quantity_kg=payload.quantity_kg,
            notes=payload.notes,
        )
    )
    db.commit()
    return calculate_mix_cost(db, mix_id)


@router.patch("/{mix_id}/ingredients/{ingredient_id}", response_model=MixRead)
def update_mix_ingredient(
    mix_id: int,
    ingredient_id: int,
    payload: MixIngredientUpdate,
    session: AuthSession = Depends(require_client_module_access("mix_master", "edit")),
    db: Session = Depends(get_db),
):
    ingredient = db.scalar(
        select(MixIngredient).where(
            MixIngredient.id == ingredient_id,
            MixIngredient.mix_id == mix_id,
            MixIngredient.tenant_id == session.tenant_id,
        )
    )
    if ingredient is None:
        raise HTTPException(status_code=404, detail="Ingredient not found")
    for field, value in payload.model_dump(exclude_unset=True).items():
        setattr(ingredient, field, value)
    db.commit()
    return calculate_mix_cost(db, mix_id)


@router.delete("/{mix_id}/ingredients/{ingredient_id}", response_model=MixRead)
def delete_mix_ingredient(mix_id: int, ingredient_id: int, session: AuthSession = Depends(require_client_module_access("mix_master", "edit")), db: Session = Depends(get_db)):
    ingredient = db.scalar(
        select(MixIngredient).where(
            MixIngredient.id == ingredient_id,
            MixIngredient.mix_id == mix_id,
            MixIngredient.tenant_id == session.tenant_id,
        )
    )
    if ingredient is None:
        raise HTTPException(status_code=404, detail="Ingredient not found")
    db.delete(ingredient)
    db.commit()
    return calculate_mix_cost(db, mix_id)


@router.get("/{mix_id}/cost-breakdown", response_model=MixRead)
def get_mix_cost_breakdown(mix_id: int, session: AuthSession = Depends(require_client_module_access("mix_master")), db: Session = Depends(get_db)):
    if db.scalar(select(Mix.id).where(Mix.id == mix_id, Mix.tenant_id == session.tenant_id)) is None:
        raise HTTPException(status_code=404, detail="Mix not found")
    return calculate_mix_cost(db, mix_id)
