"""后台积分商城管理。"""
import random
import string
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field, field_validator
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.deps import require_role
from app.database import get_db
from app.models import PromoCode, User
from app.models.coupon_mall_claim import CouponMallClaim
from app.models.coupon_mall_item import CouponMallItem, CouponMallItemScope, CouponMallItemStatus
from app.models.promo_code import PromoCodeDiscountLevel, PromoCodeScope, PromoCodeType
from app.schemas.common import ApiResponse

router = APIRouter()
_admin = require_role("admin", "super_admin")


def _generate_coupon_code(prefix: str = "PM") -> str:
    alphabet = string.ascii_uppercase + string.digits
    return prefix + "".join(random.choices(alphabet, k=10))


class AdminMallCouponCreate(BaseModel):
    code: str | None = Field(None, min_length=4, max_length=16)
    title: str = Field(..., min_length=1, max_length=200)
    subtitle: str | None = Field(None, max_length=500)
    cover_image: str | None = Field(None, max_length=500)
    description: str | None = Field(None, max_length=1000)
    category: str | None = Field(None, max_length=50)
    points_price: int = Field(..., ge=0)
    stock_total: int = Field(..., ge=1)
    sort_order: int = Field(0, ge=0)
    max_claims_per_user: int = Field(1, ge=0)
    max_claims_per_user_per_day: int = Field(0, ge=0)
    required_member_level: int = Field(0, ge=0)
    required_member_level_name: str | None = Field(None, max_length=50)
    is_hot: bool = False

    type: str = "fixed"
    value: int = Field(..., ge=1, description="fixed/threshold_fixed 为优惠金额分，percent 为折扣 1-100")
    threshold_amount: int | None = Field(None, ge=0)
    max_discount_amount: int | None = Field(None, ge=0)
    max_uses_per_user: int = Field(1, ge=1)
    min_order_amount: int | None = Field(None, ge=0)
    expires_at: datetime | None = None
    new_users_only: bool = False
    scope: str = "public"

    @field_validator("code", mode="before")
    @classmethod
    def normalize_code(cls, value: str | None) -> str | None:
        if value is None or str(value).strip() == "":
            return None
        return str(value).strip().upper()


class AdminMallItemUpdate(BaseModel):
    title: str | None = Field(None, min_length=1, max_length=200)
    subtitle: str | None = Field(None, max_length=500)
    cover_image: str | None = Field(None, max_length=500)
    description: str | None = Field(None, max_length=1000)
    category: str | None = Field(None, max_length=50)
    points_price: int | None = Field(None, ge=0)
    stock_total: int | None = Field(None, ge=0)
    stock_remaining: int | None = Field(None, ge=0)
    sort_order: int | None = Field(None, ge=0)
    max_claims_per_user: int | None = Field(None, ge=0)
    max_claims_per_user_per_day: int | None = Field(None, ge=0)
    required_member_level: int | None = Field(None, ge=0)
    required_member_level_name: str | None = Field(None, max_length=50)
    is_hot: bool | None = None
    status: str | None = Field(None, pattern=r"^(active|paused|expired)$")
    scope: str | None = Field(None, pattern=r"^(public|agent_subordinates|specific_users)$")


@router.get("/items", response_model=ApiResponse)
async def list_mall_items(
    status: str | None = Query(None),
    category: str | None = Query(None),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    db: AsyncSession = Depends(get_db),
    _: User = Depends(_admin),
):
    stmt = select(CouponMallItem, PromoCode).join(PromoCode, PromoCode.id == CouponMallItem.promo_code_id)
    if status:
        stmt = stmt.where(CouponMallItem.status == status)
    if category:
        stmt = stmt.where(CouponMallItem.category == category)
    total = await db.scalar(select(func.count()).select_from(stmt.subquery()))
    result = await db.execute(
        stmt.order_by(CouponMallItem.sort_order.desc(), CouponMallItem.id.desc()).limit(limit).offset(offset)
    )

    items = []
    for mall_item, coupon in result.all():
        issuer = await db.get(User, coupon.issuer_user_id) if coupon.issuer_user_id else None
        items.append({
            "id": mall_item.id,
            "promo_code_id": coupon.id,
            "code": coupon.code,
            "title": mall_item.title,
            "subtitle": mall_item.subtitle,
            "cover_image": mall_item.cover_image,
            "description": mall_item.description,
            "category": mall_item.category,
            "points_price": mall_item.points_price,
            "stock_total": mall_item.stock_total,
            "stock_remaining": mall_item.stock_remaining,
            "status": mall_item.status.value,
            "scope": mall_item.scope.value,
            "sort_order": mall_item.sort_order,
            "max_claims_per_user": mall_item.max_claims_per_user,
            "max_claims_per_user_per_day": mall_item.max_claims_per_user_per_day,
            "required_member_level": mall_item.required_member_level,
            "required_member_level_name": mall_item.required_member_level_name,
            "is_hot": mall_item.is_hot,
            "type": coupon.type.value if hasattr(coupon.type, "value") else str(coupon.type),
            "value": coupon.value,
            "min_order_amount": coupon.min_order_amount,
            "expires_at": coupon.expires_at.isoformat() if coupon.expires_at else None,
            "issuer_name": issuer.nickname or issuer.username if issuer else "平台",
            "created_at": mall_item.created_at.isoformat() if mall_item.created_at else None,
        })
    return ApiResponse(data={"items": items, "total": int(total or 0)})


@router.post("/items", response_model=ApiResponse)
async def create_mall_item(
    body: AdminMallCouponCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(_admin),
):
    """管理员上架积分商城优惠券商品，等价于创建一张平台优惠券并挂载商城展示。"""
    if body.type not in ("fixed", "threshold_fixed", "percent"):
        raise HTTPException(status_code=400, detail="不支持的优惠券类型")
    if body.type == "threshold_fixed" and (body.threshold_amount is None or body.threshold_amount <= 0):
        raise HTTPException(status_code=400, detail="满减券需要设置门槛")
    if body.type == "percent" and body.value > 100:
        raise HTTPException(status_code=400, detail="折扣百分比不能大于 100")

    code = body.code or _generate_coupon_code("PM")
    exists = await db.scalar(select(PromoCode.id).where(func.upper(PromoCode.code) == code.upper()).limit(1))
    if exists:
        raise HTTPException(status_code=400, detail="优惠券代码已存在")

    if body.type == "percent":
        promo_type = PromoCodeType.PERCENT.value
    elif body.type == "threshold_fixed":
        promo_type = PromoCodeType.THRESHOLD_FIXED.value
    else:
        promo_type = PromoCodeType.FIXED.value

    coupon = PromoCode(
        code=code,
        description=body.description,
        issuer_user_id=None,
        issuer_type=None,
        point_cost=0,
        point_refunded=0,
        type=promo_type,
        value=body.value,
        discount_level=PromoCodeDiscountLevel.ORDER.value,
        scope=PromoCodeScope.ALL.value,
        claim_method="mall",
        list_on_mall=True,
        expires_at=body.expires_at,
        max_uses=body.stock_total,
        max_uses_per_user=body.max_uses_per_user,
        min_order_amount=body.threshold_amount or body.min_order_amount or 0,
        max_discount_amount=body.max_discount_amount or 0,
        new_users_only=body.new_users_only,
        applies_to_renewals=True,
        is_enabled=True,
    )
    db.add(coupon)
    await db.flush()
    await db.refresh(coupon)

    scope = CouponMallItemScope.PUBLIC
    if body.scope == "agent_subordinates":
        scope = CouponMallItemScope.AGENT_SUBORDINATES

    mall_item = CouponMallItem(
        promo_code_id=coupon.id,
        issuer_user_id=0,
        title=body.title,
        subtitle=body.subtitle,
        cover_image=body.cover_image,
        description=body.description,
        points_price=body.points_price,
        stock_total=body.stock_total,
        stock_remaining=body.stock_total,
        status=CouponMallItemStatus.ACTIVE,
        scope=scope,
        sort_order=body.sort_order,
        category=body.category,
        max_claims_per_user=body.max_claims_per_user,
        max_claims_per_user_per_day=body.max_claims_per_user_per_day,
        required_member_level=body.required_member_level,
        required_member_level_name=body.required_member_level_name,
        is_hot=body.is_hot,
    )
    db.add(mall_item)
    await db.flush()
    await db.refresh(mall_item)

    return ApiResponse(data={
        "id": mall_item.id,
        "promo_code_id": coupon.id,
        "code": coupon.code,
        "title": mall_item.title,
        "stock_total": mall_item.stock_total,
        "points_price": mall_item.points_price,
    }, message="商城优惠券商品已上架")


@router.put("/items/{item_id}", response_model=ApiResponse)
async def update_mall_item(
    item_id: int,
    body: AdminMallItemUpdate,
    db: AsyncSession = Depends(get_db),
    _: User = Depends(_admin),
):
    result = await db.execute(
        select(CouponMallItem).where(CouponMallItem.id == item_id).with_for_update()
    )
    mall_item = result.scalar_one_or_none()
    if not mall_item:
        raise HTTPException(status_code=404, detail="商城商品不存在")

    if body.title is not None:
        mall_item.title = body.title
    if body.subtitle is not None:
        mall_item.subtitle = body.subtitle
    if body.cover_image is not None:
        mall_item.cover_image = body.cover_image
    if body.description is not None:
        mall_item.description = body.description
    if body.category is not None:
        mall_item.category = body.category
    if body.points_price is not None:
        mall_item.points_price = body.points_price
    if body.stock_total is not None:
        mall_item.stock_total = body.stock_total
    if body.stock_remaining is not None:
        mall_item.stock_remaining = body.stock_remaining
    if body.sort_order is not None:
        mall_item.sort_order = body.sort_order
    if body.max_claims_per_user is not None:
        mall_item.max_claims_per_user = body.max_claims_per_user
    if body.max_claims_per_user_per_day is not None:
        mall_item.max_claims_per_user_per_day = body.max_claims_per_user_per_day
    if body.required_member_level is not None:
        mall_item.required_member_level = body.required_member_level
    if body.required_member_level_name is not None:
        mall_item.required_member_level_name = body.required_member_level_name
    if body.is_hot is not None:
        mall_item.is_hot = body.is_hot
    if body.status is not None:
        mall_item.status = CouponMallItemStatus(body.status)
    if body.scope is not None:
        mall_item.scope = CouponMallItemScope(body.scope)

    await db.flush()
    await db.refresh(mall_item)
    return ApiResponse(data={"id": mall_item.id, "status": mall_item.status.value})


@router.delete("/items/{item_id}", response_model=ApiResponse)
async def delete_mall_item(
    item_id: int,
    db: AsyncSession = Depends(get_db),
    _: User = Depends(_admin),
):
    result = await db.execute(
        select(CouponMallItem, PromoCode)
        .join(PromoCode, PromoCode.id == CouponMallItem.promo_code_id)
        .where(CouponMallItem.id == item_id)
    )
    row = result.first()
    if not row:
        raise HTTPException(status_code=404, detail="商城商品不存在")
    mall_item, coupon = row

    # 如果有领取记录，仅停用，不删除
    claimed = await db.scalar(
        select(func.count(CouponMallClaim.id)).where(CouponMallClaim.mall_item_id == item_id)
    )
    if claimed:
        mall_item.status = CouponMallItemStatus.PAUSED
        coupon.is_enabled = False
        await db.flush()
        return ApiResponse(message="该商品已有领取记录，已停用而非删除")

    await db.delete(mall_item)
    await db.delete(coupon)
    await db.flush()
    return ApiResponse(message="商城商品已删除")


@router.get("/claims", response_model=ApiResponse)
async def list_mall_claims(
    status: str | None = Query(None),
    mall_item_id: int | None = Query(None),
    limit: int = Query(50, ge=1, le=200),
    offset: int = Query(0, ge=0),
    db: AsyncSession = Depends(get_db),
    _: User = Depends(_admin),
):
    """积分商城领取/使用流水（订单流水）。"""
    stmt = (
        select(CouponMallClaim, PromoCode, CouponMallItem, User)
        .join(PromoCode, PromoCode.id == CouponMallClaim.promo_code_id)
        .join(CouponMallItem, CouponMallItem.id == CouponMallClaim.mall_item_id)
        .join(User, User.id == CouponMallClaim.user_id)
        .order_by(CouponMallClaim.id.desc())
    )
    if status:
        stmt = stmt.where(CouponMallClaim.status == status)
    if mall_item_id:
        stmt = stmt.where(CouponMallClaim.mall_item_id == mall_item_id)

    total = await db.scalar(select(func.count()).select_from(stmt.subquery()))
    result = await db.execute(stmt.limit(limit).offset(offset))

    items = []
    for claim, coupon, mall_item, user in result.all():
        items.append({
            "id": claim.id,
            "user_id": user.id,
            "username": user.username,
            "nickname": user.nickname,
            "mall_item_id": mall_item.id,
            "mall_item_title": mall_item.title,
            "promo_code_id": coupon.id,
            "coupon_description": coupon.description,
            "coupon_type": coupon.type.value if hasattr(coupon.type, "value") else str(coupon.type),
            "coupon_value": coupon.value,
            "points_paid": claim.points_paid,
            "status": claim.status.value,
            "used_at": claim.used_at.isoformat() if claim.used_at else None,
            "order_id": claim.order_id,
            "created_at": claim.created_at.isoformat() if claim.created_at else None,
        })
    return ApiResponse(data={"items": items, "total": int(total or 0)})
