from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session, selectinload

from app.database import get_db
from app.deps import require_permission
from app.models import EventProduct, Product, ProductCategory, QuotationItem, User
from app.schemas import ProductCategoryIn, ProductCategoryOut, ProductIn, ProductOut
from app.utils import next_public_id, unique_product_slug

router = APIRouter(tags=["products"])


def serialize_product(row: Product) -> ProductOut:
    return ProductOut(
        id=row.id,
        public_id=row.public_id,
        slug=row.slug,
        name=row.name,
        category_id=row.category_id,
        category_name=row.category.name if row.category else None,
        description=row.description,
        base_price=row.base_price,
        cost=row.cost,
        service_duration=row.service_duration,
        image_url=row.image_url,
        video_url=row.video_url,
        is_active=row.is_active,
    )


def _apply_category(db: Session, data: dict) -> dict:
    name = (data.pop("category_name", None) or "").strip()
    if name:
        category = db.query(ProductCategory).filter(ProductCategory.name == name).first()
        if category is None:
            category = ProductCategory(name=name, is_active=True)
            db.add(category)
            db.flush()
        data["category_id"] = category.id
    return data


def _unique_product_code(db: Session, public_id: str, exclude_id: int | None = None) -> None:
    query = db.query(Product).filter(Product.public_id == public_id)
    if exclude_id is not None:
        query = query.filter(Product.id != exclude_id)
    if query.first():
        raise HTTPException(status_code=400, detail="Product code already exists")


def _resolve_slug(db: Session, data: dict, *, name: str, public_id: str, exclude_id: int | None = None) -> str:
    raw = (data.pop("slug", None) or "").strip()
    base = raw or name or public_id
    return unique_product_slug(db, base, exclude_id=exclude_id)


@router.get("/product-categories", response_model=list[ProductCategoryOut])
def list_categories(db: Session = Depends(get_db), _: User = Depends(require_permission("product.view"))):
    return db.query(ProductCategory).all()


@router.get("/product-categories/{category_id}", response_model=ProductCategoryOut)
def get_category(category_id: int, db: Session = Depends(get_db), _: User = Depends(require_permission("product.view"))):
    row = db.query(ProductCategory).filter(ProductCategory.id == category_id).first()
    if row is None:
        raise HTTPException(status_code=404, detail="Category not found")
    return row


@router.post("/product-categories", response_model=ProductCategoryOut)
def create_category(payload: ProductCategoryIn, db: Session = Depends(get_db), _: User = Depends(require_permission("product.create"))):
    row = ProductCategory(**payload.model_dump())
    db.add(row)
    db.commit()
    db.refresh(row)
    return row


@router.patch("/product-categories/{category_id}", response_model=ProductCategoryOut)
def update_category(category_id: int, payload: ProductCategoryIn, db: Session = Depends(get_db), _: User = Depends(require_permission("product.edit"))):
    row = db.query(ProductCategory).filter(ProductCategory.id == category_id).first()
    if row is None:
        raise HTTPException(status_code=404, detail="Category not found")
    for key, value in payload.model_dump().items():
        setattr(row, key, value)
    db.commit()
    db.refresh(row)
    return row


@router.get("/products", response_model=list[ProductOut])
def list_products(db: Session = Depends(get_db), _: User = Depends(require_permission("product.view"))):
    rows = db.query(Product).options(selectinload(Product.category)).order_by(Product.public_id.asc()).all()
    return [serialize_product(row) for row in rows]


@router.get("/products/{product_id}", response_model=ProductOut)
def get_product(product_id: int, db: Session = Depends(get_db), _: User = Depends(require_permission("product.view"))):
    row = db.query(Product).options(selectinload(Product.category)).filter(Product.id == product_id).first()
    if row is None:
        raise HTTPException(status_code=404, detail="Product not found")
    return serialize_product(row)


@router.post("/products", response_model=ProductOut)
def create_product(payload: ProductIn, db: Session = Depends(get_db), _: User = Depends(require_permission("product.create"))):
    data = _apply_category(db, payload.model_dump())
    public_id = (data.pop("public_id") or "").strip() or next_public_id(db, Product, "PRD")
    _unique_product_code(db, public_id)
    slug = _resolve_slug(db, data, name=data.get("name") or "", public_id=public_id)
    row = Product(public_id=public_id, slug=slug, **data)
    db.add(row)
    db.commit()
    row = db.query(Product).options(selectinload(Product.category)).filter(Product.id == row.id).first()
    return serialize_product(row)


@router.patch("/products/{product_id}", response_model=ProductOut)
def update_product(product_id: int, payload: ProductIn, db: Session = Depends(get_db), _: User = Depends(require_permission("product.edit"))):
    row = db.query(Product).filter(Product.id == product_id).first()
    if row is None:
        raise HTTPException(status_code=404, detail="Product not found")
    data = _apply_category(db, payload.model_dump())
    public_id = data.pop("public_id")
    if public_id:
        public_id = public_id.strip()
        _unique_product_code(db, public_id, exclude_id=row.id)
        row.public_id = public_id
    slug = _resolve_slug(
        db,
        data,
        name=data.get("name") or row.name or "",
        public_id=row.public_id,
        exclude_id=row.id,
    )
    row.slug = slug
    for key, value in data.items():
        setattr(row, key, value)
    db.commit()
    row = db.query(Product).options(selectinload(Product.category)).filter(Product.id == row.id).first()
    return serialize_product(row)


@router.delete("/product-categories/{category_id}")
def delete_category(category_id: int, db: Session = Depends(get_db), _: User = Depends(require_permission("product.delete"))):
    row = db.query(ProductCategory).filter(ProductCategory.id == category_id).first()
    if row is None:
        raise HTTPException(status_code=404, detail="Category not found")
    db.query(Product).filter(Product.category_id == category_id).update({Product.category_id: None})
    db.delete(row)
    db.commit()
    return {"ok": True}


@router.delete("/products/{product_id}")
def delete_product(product_id: int, db: Session = Depends(get_db), _: User = Depends(require_permission("product.delete"))):
    row = db.query(Product).filter(Product.id == product_id).first()
    if row is None:
        raise HTTPException(status_code=404, detail="Product not found")
    db.query(EventProduct).filter(EventProduct.product_id == product_id).update({EventProduct.product_id: None})
    db.query(QuotationItem).filter(QuotationItem.product_id == product_id).update({QuotationItem.product_id: None})
    try:
        db.delete(row)
        db.commit()
    except IntegrityError as exc:
        db.rollback()
        row.is_active = False
        db.commit()
        raise HTTPException(status_code=400, detail="Product is in use, so it was deactivated instead") from exc
    return {"ok": True}
