"""商品价格规则 — Admin 路由（挂载到 /api/products）

GET  /api/products/{product_id}/price-rules   读取规则集
PUT  /api/products/{product_id}/price-rules   原子替换整组规则（乐观锁 + 触发 POS revision bump）

PUT 接受客户端携带的 price_rules_version，版本不匹配返回 409。替换成功后：
  - Product.price_rules_version += 1
  - 通过 bump_revision 触发 POS catalogue revision +1（POS Agent 下次刷新即可拉到新规则）

租户隔离：所有读写都强制 Product.tenant_id == current_user.tenant_id。
"""
from __future__ import annotations

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete

from app.api.deps import get_db, get_admin_user
from app.core.models.user import User
from app.core.models.product import Product, ProductPriceRule
from app.core.models.member import MemberLevel
from app.plugins.pos_sync.revision import bump_revision
from app.schemas.product_price_rule import (
    ProductPriceRuleIn,
    ProductPriceRulesReplace,
    ProductPriceRulesResponse,
    ProductPriceRuleOut,
    normalize_rule_windows,
)
from app.services.audit import log_audit

router = APIRouter(prefix="/products", tags=["商品管理"])


async def _verify_product_owned(
    db: AsyncSession, user: User, product_id: int, *, for_update: bool = False,
) -> Product:
    """校验商品存在且属于当前租户。"""
    stmt = select(Product).where(Product.id == product_id, Product.tenant_id == user.tenant_id)
    if for_update:
        stmt = stmt.with_for_update()
    result = await db.execute(stmt)
    p = result.scalar_one_or_none()
    if p is None:
        raise HTTPException(status_code=404, detail="商品不存在")
    return p


async def _verify_variant_in_tenant(
    db: AsyncSession, tenant_id: int, variant_id: int | None, product_id: int
) -> None:
    """校验 variant 存在且属于本租户 / 本商品。"""
    if variant_id is None:
        return
    from app.core.models.product import ProductVariant
    row = (await db.execute(
        select(ProductVariant.id).where(
            ProductVariant.id == variant_id,
            ProductVariant.tenant_id == tenant_id,
            ProductVariant.product_id == product_id,
        )
    )).scalar_one_or_none()
    if row is None:
        raise HTTPException(status_code=422, detail="variant_id 不属于该商品或租户")


async def _verify_member_level_in_tenant(
    db: AsyncSession, tenant_id: int, member_level_id: int | None
) -> None:
    if member_level_id is None:
        return
    row = (await db.execute(
        select(MemberLevel.id).where(
            MemberLevel.id == member_level_id,
            MemberLevel.tenant_id == tenant_id,
            MemberLevel.is_active == 1,
        )
    )).scalar_one_or_none()
    if row is None:
        raise HTTPException(status_code=422, detail="member_level_id 不属于该租户或未启用")


@router.get("/{product_id}/price-rules", response_model=ProductPriceRulesResponse)
async def get_price_rules(
    product_id: int,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_admin_user),
):
    product = await _verify_product_owned(db, user, product_id)
    rows = (await db.execute(
        select(ProductPriceRule).where(
            ProductPriceRule.tenant_id == user.tenant_id,
            ProductPriceRule.product_id == product_id,
        ).order_by(
            ProductPriceRule.min_quantity.desc(),
            ProductPriceRule.priority.asc(),
            ProductPriceRule.id.asc(),
        )
    )).scalars().all()

    return ProductPriceRulesResponse(
        product_id=product_id,
        price_rules_version=product.price_rules_version or 0,
        rules=[ProductPriceRuleOut.model_validate(r) for r in rows],
    )


@router.put("/{product_id}/price-rules", response_model=ProductPriceRulesResponse)
async def replace_price_rules(
    product_id: int,
    body: ProductPriceRulesReplace,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_admin_user),
):
    """原子替换单个商品的整组规则。
    - price_rules_version 不匹配 → 409
    - 替换成功后递增版本号与 POS catalogue revision
    - 单事务 commit，所有校验一致失败就回滚
    """
    product = await _verify_product_owned(db, user, product_id, for_update=True)
    if body.price_rules_version != (product.price_rules_version or 0):
        raise HTTPException(
            status_code=409,
            detail=f"价格规则版本不匹配（服务端={product.price_rules_version or 0}, 客户端={body.price_rules_version}）",
        )

    tenant_id = user.tenant_id

    # 校验所有 variant_id / member_level_id 都属于当前租户
    for rule in body.rules:
        await _verify_variant_in_tenant(db, tenant_id, rule.variant_id, product_id)
        await _verify_member_level_in_tenant(db, tenant_id, rule.member_level_id)

    # 删除旧规则；新增当前提交的；按相同编号（id 在入参里）走"更新而非新增"
    await db.execute(
        delete(ProductPriceRule).where(
            ProductPriceRule.tenant_id == tenant_id,
            ProductPriceRule.product_id == product_id,
        )
    )
    await db.flush()

    new_rules: list[ProductPriceRule] = []
    for rule in body.rules:
        starts_at, ends_at = normalize_rule_windows(rule)
        pr = ProductPriceRule(
            tenant_id=tenant_id,
            product_id=product_id,
            variant_id=rule.variant_id,
            member_level_id=rule.member_level_id,
            channel_scope=rule.channel_scope,
            min_quantity=rule.min_quantity,
            price_type=rule.price_type,
            price_value=rule.price_value,
            priority=rule.priority,
            is_promotion=1 if rule.is_promotion else 0,
            starts_at=starts_at,
            ends_at=ends_at,
            is_active=1 if rule.is_active else 0,
        )
        new_rules.append(pr)
        db.add(pr)

    product.price_rules_version = (product.price_rules_version or 0) + 1

    # bump POS catalogue revision —— 让在线 POS Agent 拉到新规则
    await bump_revision(db, tenant_id)

    # 审计与业务写入同一事务：审计失败则整体回滚，保证改价一定有审计痕迹
    await log_audit(
        db=db,
        tenant_id=tenant_id,
        action="update_price_rules",
        actor_type="admin",
        actor_id=user.id,
        actor_name=getattr(user, "name", None),
        target_type="product",
        target_id=product_id,
        changes={"rules_count": len(new_rules)},
    )

    try:
        await db.commit()
    except Exception:
        await db.rollback()
        raise

    await db.refresh(product)

    rows = (await db.execute(
        select(ProductPriceRule).where(
            ProductPriceRule.tenant_id == tenant_id,
            ProductPriceRule.product_id == product_id,
        ).order_by(
            ProductPriceRule.min_quantity.desc(),
            ProductPriceRule.priority.asc(),
            ProductPriceRule.id.asc(),
        )
    )).scalars().all()

    return ProductPriceRulesResponse(
        product_id=product_id,
        price_rules_version=product.price_rules_version,
        rules=[ProductPriceRuleOut.model_validate(r) for r in rows],
    )
