import json
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import Optional

from database import get_db
from models.waste import WasteLog
from models.product import Product
from models.business_day import BusinessDay
from models.user import User
from schemas.waste import WasteLogCreate, WasteLogOut, VALID_REASONS, REASON_LABELS
from routers.deps import require_manager

router = APIRouter()


def _effective_cost(product: Product) -> Optional[float]:
    """Return product's current effective cost (breakdown sum or simple), or None."""
    if product.cost_breakdown:
        try:
            entries = json.loads(product.cost_breakdown)
            total = sum(e.get("amount", 0.0) for e in entries if isinstance(e, dict))
            if total > 0:
                return total
        except Exception:
            pass
    if product.cost_simple:
        return product.cost_simple
    return None


def _waste_out(w: WasteLog) -> dict:
    product_name = w.product.name if w.product else f"#{w.product_id}"
    logger_name = None
    if w.logged_by:
        logger_name = w.logged_by.full_name or w.logged_by.username
    return {
        "id": w.id,
        "product_id": w.product_id,
        "product_name": product_name,
        "quantity": w.quantity,
        "unit_cost_snapshot": w.unit_cost_snapshot,
        "total_cost": w.total_cost,
        "reason": w.reason,
        "reason_label": REASON_LABELS.get(w.reason, w.reason),
        "reason_notes": w.reason_notes,
        "logged_by_id": w.logged_by_id,
        "logged_by_name": logger_name,
        "business_day_id": w.business_day_id,
        "logged_at": w.logged_at,
    }


@router.get("/")
def list_waste(
    business_day_id: Optional[int] = None,
    from_dt: Optional[str] = Query(default=None, alias="from"),
    to_dt: Optional[str] = Query(default=None, alias="to"),
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    from datetime import datetime
    q = db.query(WasteLog).order_by(WasteLog.logged_at.desc())
    if business_day_id:
        q = q.filter(WasteLog.business_day_id == business_day_id)
    elif from_dt and to_dt:
        q = q.filter(
            WasteLog.logged_at >= datetime.fromisoformat(from_dt),
            WasteLog.logged_at <= datetime.fromisoformat(to_dt),
        )
    entries = q.all()
    return {"entries": [_waste_out(w) for w in entries]}


@router.post("/", status_code=status.HTTP_201_CREATED)
def log_waste(
    body: WasteLogCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    if body.reason not in VALID_REASONS:
        raise HTTPException(status_code=400, detail=f"Invalid reason. Valid: {sorted(VALID_REASONS)}")
    if body.quantity <= 0:
        raise HTTPException(status_code=400, detail="Quantity must be positive")

    product = db.query(Product).filter(Product.id == body.product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")

    # Snapshot current effective cost
    unit_cost = _effective_cost(product)
    total_cost = round(unit_cost * body.quantity, 2) if unit_cost is not None else None

    # Attach to open business day if any
    active_day = db.query(BusinessDay).filter(BusinessDay.status == "open").first()

    entry = WasteLog(
        product_id=body.product_id,
        quantity=body.quantity,
        reason=body.reason,
        reason_notes=body.reason_notes,
        unit_cost_snapshot=unit_cost,
        total_cost=total_cost,
        logged_by_id=user.id,
        business_day_id=active_day.id if active_day else None,
    )
    db.add(entry)
    db.commit()
    db.refresh(entry)
    return _waste_out(entry)


@router.delete("/{entry_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_waste(
    entry_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    entry = db.query(WasteLog).filter(WasteLog.id == entry_id).first()
    if not entry:
        raise HTTPException(status_code=404, detail="Waste entry not found")

    # Only allow deletion within the same business day
    active_day = db.query(BusinessDay).filter(BusinessDay.status == "open").first()
    if active_day and entry.business_day_id != active_day.id:
        raise HTTPException(status_code=403, detail="Can only delete waste entries from the current open business day")

    db.delete(entry)
    db.commit()


@router.get("/summary")
def waste_summary(
    business_day_id: Optional[int] = None,
    from_dt: Optional[str] = Query(default=None, alias="from"),
    to_dt: Optional[str] = Query(default=None, alias="to"),
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    from datetime import datetime
    q = db.query(WasteLog)
    if business_day_id:
        q = q.filter(WasteLog.business_day_id == business_day_id)
    elif from_dt and to_dt:
        q = q.filter(
            WasteLog.logged_at >= datetime.fromisoformat(from_dt),
            WasteLog.logged_at <= datetime.fromisoformat(to_dt),
        )
    entries = q.all()

    total_items = len(entries)
    total_cost = round(sum(e.total_cost for e in entries if e.total_cost is not None), 2)
    uncosted_count = sum(1 for e in entries if e.total_cost is None)

    by_reason: dict = {}
    by_product: dict = {}
    for e in entries:
        r = e.reason
        if r not in by_reason:
            by_reason[r] = {"reason": r, "label": REASON_LABELS.get(r, r), "count": 0, "total_cost": 0.0}
        by_reason[r]["count"] += 1
        by_reason[r]["total_cost"] += e.total_cost or 0.0

        pid = e.product_id
        pname = e.product.name if e.product else f"#{pid}"
        if pid not in by_product:
            by_product[pid] = {"product_id": pid, "product_name": pname, "qty": 0.0, "total_cost": 0.0}
        by_product[pid]["qty"] += e.quantity
        by_product[pid]["total_cost"] += e.total_cost or 0.0

    for v in by_reason.values():
        v["total_cost"] = round(v["total_cost"], 2)
    for v in by_product.values():
        v["total_cost"] = round(v["total_cost"], 2)

    return {
        "total_items": total_items,
        "total_cost": total_cost,
        "uncosted_count": uncosted_count,
        "by_reason": sorted(by_reason.values(), key=lambda x: -x["count"]),
        "by_product": sorted(by_product.values(), key=lambda x: -x["total_cost"]),
    }
