"""购物车路由 — 登录用户管理购物车项。"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.deps import get_current_user
from app.database import get_db
from app.models.cart_item import CartItem
from app.models.product import Product, ProductApiType, ProductStatus
from app.models.user import User
from app.schemas.cart import (
    CartItemCreate,
    CartItemResponse,
    CartItemUpdate,
    CartProductSummary,
)
from app.schemas.common import ApiResponse
from app.services import provision_plugin_service

router = APIRouter()


def _ensure_product_can_be_added(product: Product | None, quantity: int, reserved_quantity: int = 0) -> Product:
    if not product or product.status != ProductStatus.ACTIVE.value:
        raise HTTPException(status_code=404, detail="产品不存在或已下架")

    if product.stock_policy == "tracked":
        available = int(product.stock or 0)
        required = reserved_quantity + quantity
        if available <= 0:
            raise HTTPException(status_code=409, detail=f"产品「{product.name}」已售罄")
        if required > available:
            detail = f"产品「{product.name}」库存不足，当前仅剩 {available} 台"
            if reserved_quantity > 0:
                detail += f"，购物车已选择 {reserved_quantity} 台"
            raise HTTPException(
                status_code=409,
                detail=detail,
            )
    return product


def _enum_value(value) -> str:
    return str(value.value if hasattr(value, "value") else value)


def _validate_plugin_cart_fields(product: Product, slider_values: dict | None) -> None:
    if _enum_value(product.api_type) != ProductApiType.PLUGIN.value:
        return
    plugin_id = str(product.api_identifier or "").strip()
    if not plugin_id:
        raise HTTPException(status_code=400, detail="插件商品未绑定开通插件")
    provision_plugin_service.validate_purchase_fields(plugin_id, slider_values)


async def _cart_reserved_quantity(
    db: AsyncSession,
    user_id: int,
    product_id: int,
    *,
    exclude_item_id: int | None = None,
) -> int:
    stmt = select(CartItem.quantity).where(
        CartItem.user_id == user_id,
        CartItem.product_id == product_id,
    )
    if exclude_item_id is not None:
        stmt = stmt.where(CartItem.id != exclude_item_id)
    res = await db.execute(stmt)
    return sum(int(quantity or 0) for quantity in res.scalars().all())


async def _build_response(db: AsyncSession, item: CartItem) -> CartItemResponse:
    res = CartItemResponse.model_validate(item)
    prod_res = await db.execute(select(Product).where(Product.id == item.product_id))
    prod = prod_res.scalar_one_or_none()
    if prod:
        res.product = CartProductSummary.model_validate(prod)
    return res


@router.get("", response_model=ApiResponse)
async def list_cart_items(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    res = await db.execute(
        select(CartItem)
        .where(CartItem.user_id == current_user.id)
        .order_by(CartItem.created_at.desc())
    )
    items = res.scalars().all()
    out = [await _build_response(db, item) for item in items]
    return ApiResponse(data=out)


@router.post("", response_model=ApiResponse)
async def add_cart_item(
    data: CartItemCreate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    prod_res = await db.execute(select(Product).where(Product.id == data.product_id))
    prod = prod_res.scalar_one_or_none()
    reserved_quantity = await _cart_reserved_quantity(db, current_user.id, data.product_id)
    product = _ensure_product_can_be_added(prod, data.quantity, reserved_quantity)
    _validate_plugin_cart_fields(product, data.slider_values)

    item = CartItem(
        user_id=current_user.id,
        **data.model_dump(),
    )
    db.add(item)
    await db.flush()
    await db.refresh(item)
    return ApiResponse(data=await _build_response(db, item))


@router.patch("/{item_id}", response_model=ApiResponse)
async def update_cart_item(
    item_id: int,
    data: CartItemUpdate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    res = await db.execute(
        select(CartItem).where(
            CartItem.id == item_id, CartItem.user_id == current_user.id
        )
    )
    item = res.scalar_one_or_none()
    if not item:
        raise HTTPException(status_code=404, detail="购物车项不存在")

    update_data = data.model_dump(exclude_unset=True)
    if "quantity" in update_data:
        prod_res = await db.execute(select(Product).where(Product.id == item.product_id))
        prod = prod_res.scalar_one_or_none()
        reserved_quantity = await _cart_reserved_quantity(
            db,
            current_user.id,
            item.product_id,
            exclude_item_id=item.id,
        )
        product = _ensure_product_can_be_added(prod, int(update_data["quantity"]), reserved_quantity)
        _validate_plugin_cart_fields(product, item.slider_values)

    for k, v in update_data.items():
        setattr(item, k, v)
    await db.flush()
    await db.refresh(item)
    return ApiResponse(data=await _build_response(db, item))


@router.delete("/{item_id}", response_model=ApiResponse)
async def delete_cart_item(
    item_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    res = await db.execute(
        select(CartItem).where(
            CartItem.id == item_id, CartItem.user_id == current_user.id
        )
    )
    item = res.scalar_one_or_none()
    if not item:
        raise HTTPException(status_code=404, detail="购物车项不存在")
    await db.delete(item)
    return ApiResponse(data=None)


@router.delete("", response_model=ApiResponse)
async def clear_cart(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    res = await db.execute(
        select(CartItem).where(CartItem.user_id == current_user.id)
    )
    items = res.scalars().all()
    for item in items:
        await db.delete(item)
    return ApiResponse(data={"deleted": len(items)})
