from fastapi import HTTPException
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import defer

from app.models.product import Product
from app.models.product_category import ProductCategory
from app.models.product_group import ProductGroup
from app.schemas.product_group import (
    ProductCategoryCreate,
    ProductCategoryUpdate,
    ProductGroupCreate,
    ProductGroupUpdate,
)


# ---------- categories ----------


async def list_categories(db: AsyncSession) -> list[ProductCategory]:
    res = await db.execute(
        select(ProductCategory).order_by(
            ProductCategory.sort_order.asc(), ProductCategory.id.asc()
        )
    )
    return list(res.scalars().all())


async def create_category(db: AsyncSession, data: ProductCategoryCreate) -> ProductCategory:
    if await _slug_exists(db, ProductCategory, data.slug):
        raise HTTPException(status_code=400, detail=f"slug 已存在：{data.slug}")
    cat = ProductCategory(**data.model_dump())
    db.add(cat)
    await db.flush()
    await db.refresh(cat)
    return cat


async def update_category(
    db: AsyncSession, cat_id: int, data: ProductCategoryUpdate
) -> ProductCategory:
    cat = await _get_or_404(db, ProductCategory, cat_id, "分类不存在")
    for k, v in data.model_dump(exclude_unset=True).items():
        setattr(cat, k, v)
    await db.flush()
    await db.refresh(cat)
    return cat


async def delete_category(db: AsyncSession, cat_id: int) -> None:
    cat = await _get_or_404(db, ProductCategory, cat_id, "分类不存在")
    cnt = await db.scalar(
        select(func.count()).select_from(ProductGroup).where(ProductGroup.category_id == cat_id)
    )
    if cnt:
        raise HTTPException(status_code=400, detail=f"该分类下还有 {cnt} 个产品组，无法删除")
    await db.delete(cat)
    await db.flush()


# ---------- groups ----------


async def list_groups(
    db: AsyncSession,
    category_id: int | None = None,
    include_os_options: bool = True,
) -> list[ProductGroup]:
    stmt = select(ProductGroup).order_by(
        ProductGroup.category_id.asc(), ProductGroup.sort_order.asc(), ProductGroup.id.asc()
    )
    if not include_os_options:
        stmt = stmt.options(defer(ProductGroup.os_options))
    if category_id is not None:
        stmt = stmt.where(ProductGroup.category_id == category_id)
    res = await db.execute(stmt)
    return list(res.scalars().all())


async def get_os_options_page(
    db: AsyncSession,
    group_id: int,
    offset: int,
    limit: int,
) -> dict:
    grp = await _get_or_404(db, ProductGroup, group_id, "产品组不存在")
    options = grp.os_options if isinstance(grp.os_options, list) else []
    total = len(options)
    items = []
    for index, item in enumerate(options[offset : offset + limit], start=offset):
        if isinstance(item, dict):
            items.append({**item, "index": index})
        else:
            items.append({"label": str(item), "value": 0, "index": index})
    return {"items": items, "total": total, "offset": offset, "limit": limit}


async def replace_os_options_page(
    db: AsyncSession,
    group_id: int,
    offset: int,
    limit: int,
    items: list,
) -> dict:
    grp = await _get_or_404(db, ProductGroup, group_id, "产品组不存在")
    options = list(grp.os_options) if isinstance(grp.os_options, list) else []
    normalized = []
    for item in items:
        if isinstance(item, dict):
            normalized.append({
                "label": str(item.get("label") or "").strip(),
                "value": int(item.get("value") or 0),
            })
    normalized = [item for item in normalized if item["label"]]
    options[offset : offset + limit] = normalized
    grp.os_options = options
    await db.flush()
    await db.refresh(grp)
    total = len(options)
    page_items = []
    for index, item in enumerate(options[offset : offset + limit], start=offset):
        if isinstance(item, dict):
            page_items.append({**item, "index": index})
    return {"items": page_items, "total": total, "offset": offset, "limit": limit}


async def create_group(db: AsyncSession, data: ProductGroupCreate) -> ProductGroup:
    await _get_or_404(db, ProductCategory, data.category_id, "分类不存在")
    exists = await db.scalar(
        select(ProductGroup).where(
            ProductGroup.category_id == data.category_id,
            ProductGroup.slug == data.slug,
        )
    )
    if exists:
        raise HTTPException(status_code=400, detail=f"该分类下已有 slug={data.slug}")
    grp = ProductGroup(**data.model_dump())
    db.add(grp)
    await db.flush()
    await db.refresh(grp)
    return grp


async def update_group(db: AsyncSession, group_id: int, data: ProductGroupUpdate) -> ProductGroup:
    grp = await _get_or_404(db, ProductGroup, group_id, "产品组不存在")
    update = data.model_dump(exclude_unset=True)
    if "os_options" in update:
        incoming_options = update["os_options"] if isinstance(update["os_options"], list) else []
        existing_options = grp.os_options if isinstance(grp.os_options, list) else []
        is_basic_update = bool(set(update) - {"os_options"})
        if is_basic_update and existing_options and not incoming_options:
            update.pop("os_options", None)
    if "category_id" in update and update["category_id"] != grp.category_id:
        await _get_or_404(db, ProductCategory, update["category_id"], "分类不存在")
    for k, v in update.items():
        setattr(grp, k, v)
    await db.flush()
    await db.refresh(grp)
    return grp


async def update_group_verification_rule(
    db: AsyncSession,
    group_id: int,
    *,
    requires_real_name: bool,
    apply_to_products: bool = True,
) -> dict:
    grp = await _get_or_404(db, ProductGroup, group_id, "产品组不存在")
    grp.requires_real_name = bool(requires_real_name)
    updated_products = 0
    if apply_to_products:
        products_res = await db.execute(select(Product).where(Product.group_id == group_id))
        products = list(products_res.scalars().all())
        for product in products:
            product.requires_real_name = bool(requires_real_name)
        updated_products = len(products)
    await db.flush()
    await db.refresh(grp)
    return {"group": grp, "updated_products": updated_products}


async def delete_group(db: AsyncSession, group_id: int) -> None:
    grp = await _get_or_404(db, ProductGroup, group_id, "产品组不存在")
    cnt = await db.scalar(
        select(func.count()).select_from(Product).where(Product.group_id == group_id)
    )
    if cnt:
        raise HTTPException(status_code=400, detail=f"该组下还有 {cnt} 个产品，无法删除")
    await db.delete(grp)
    await db.flush()


# ---------- helpers ----------


async def _get_or_404(db: AsyncSession, model, pk: int, msg: str):
    res = await db.execute(select(model).where(model.id == pk))
    obj = res.scalar_one_or_none()
    if not obj:
        raise HTTPException(status_code=404, detail=msg)
    return obj


async def _slug_exists(db: AsyncSession, model, slug: str) -> bool:
    return bool(
        await db.scalar(select(func.count()).select_from(model).where(model.slug == slug))
    )
