package app

import (
	"fmt"
	"net/http"
	"time"

	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/validation"
	"bindbox-game/internal/repository/mysql/model"
	"gorm.io/gorm/clause"
)

// ==================== 用户次数卡 API ====================

type getGamePassesRequest struct {
	ActivityID *int64 `form:"activity_id"`
}

type userGamePassItem struct {
	ID           int64  `json:"id"`
	ActivityID   int64  `json:"activity_id"`
	ActivityName string `json:"activity_name"`
	Remaining    int32  `json:"remaining"`
	ExpiredAt    string `json:"expired_at"`
	Source       string `json:"source"`
}

type getGamePassesResponse struct {
	TotalRemaining  int32              `json:"total_remaining"`
	GlobalRemaining int32              `json:"global_remaining"` // 全局通用次数
	Passes          []userGamePassItem `json:"passes"`
}

// GetGamePasses 获取用户可用的游戏次数卡
// @Summary 获取用户次数卡
// @Description 查询当前用户可用的游戏次数卡
// @Tags APP端.用户
// @Accept json
// @Produce json
// @Security LoginVerifyToken
// @Param activity_id query integer false "活动ID，不传返回所有可用次数卡"
// @Success 200 {object} getGamePassesResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/game-passes/available [get]
func (h *handler) GetGamePasses() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(getGamePassesRequest)
		res := new(getGamePassesResponse)
		if err := ctx.ShouldBindForm(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		userID := int64(ctx.SessionUserInfo().Id)
		now := time.Now()

		// 查询用户的次数卡（已过滤过期和剩余为0的）
		q := h.readDB.UserGamePasses.WithContext(ctx.RequestContext()).
			Where(h.readDB.UserGamePasses.UserID.Eq(userID)).
			Where(h.readDB.UserGamePasses.Remaining.Gt(0))

		passes, err := q.Find()
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error()))
			return
		}

		// 获取活动名称
		activityIDs := make([]int64, 0)
		for _, p := range passes {
			if p.ActivityID > 0 {
				activityIDs = append(activityIDs, p.ActivityID)
			}
		}
		activityMap := make(map[int64]string)
		if len(activityIDs) > 0 {
			activities, _ := h.readDB.Activities.WithContext(ctx.RequestContext()).
				Where(h.readDB.Activities.ID.In(activityIDs...)).Find()
			for _, a := range activities {
				activityMap[a.ID] = a.Name
			}
		}

		res.Passes = make([]userGamePassItem, 0)
		for _, p := range passes {
			// 检查是否过期
			if !p.ExpiredAt.IsZero() && p.ExpiredAt.Before(now) {
				continue
			}

			// 如果指定了活动ID，只返回全局通用的和该活动的
			if req.ActivityID != nil && *req.ActivityID > 0 {
				if p.ActivityID != 0 && p.ActivityID != *req.ActivityID {
					continue
				}
			}

			expiredAt := ""
			if !p.ExpiredAt.IsZero() {
				expiredAt = p.ExpiredAt.Format("2006-01-02 15:04:05")
			}

			item := userGamePassItem{
				ID:           p.ID,
				ActivityID:   p.ActivityID,
				ActivityName: activityMap[p.ActivityID],
				Remaining:    p.Remaining,
				ExpiredAt:    expiredAt,
				Source:       p.Source,
			}

			res.TotalRemaining += p.Remaining
			if p.ActivityID == 0 {
				res.GlobalRemaining += p.Remaining
			}
			res.Passes = append(res.Passes, item)
		}

		ctx.Payload(res)
	}
}

// ==================== 套餐列表 API ====================

type getPackagesRequest struct {
	ActivityID *int64 `form:"activity_id"`
}

type packageItem struct {
	ID            int64  `json:"id"`
	Name          string `json:"name"`
	PassCount     int32  `json:"pass_count"`
	Price         int64  `json:"price"`
	OriginalPrice int64  `json:"original_price"`
	ValidDays     int32  `json:"valid_days"`
	ActivityID    int64  `json:"activity_id"`
}

type getPackagesResponse struct {
	Packages []packageItem `json:"packages"`
}

// GetGamePassPackages 获取可购买的套餐列表
// @Summary 获取次数卡套餐
// @Description 获取可购买的次数卡套餐列表
// @Tags APP端.用户
// @Accept json
// @Produce json
// @Param activity_id query integer false "活动ID，不传返回全局套餐"
// @Success 200 {object} getPackagesResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/game-passes/packages [get]
func (h *handler) GetGamePassPackages() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(getPackagesRequest)
		res := new(getPackagesResponse)
		if err := ctx.ShouldBindForm(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		q := h.readDB.GamePassPackages.WithContext(ctx.RequestContext()).
			Where(h.readDB.GamePassPackages.Status.Eq(1)) // 只返回上架的

		// 如果指定了活动ID，返回全局套餐(activity_id=0)和该活动的专属套餐
		if req.ActivityID != nil && *req.ActivityID > 0 {
			q = q.Where(h.readDB.GamePassPackages.ActivityID.In(0, *req.ActivityID))
		} else {
			// 只返回全局套餐
			q = q.Where(h.readDB.GamePassPackages.ActivityID.Eq(0))
		}

		packages, err := q.Order(h.readDB.GamePassPackages.SortOrder.Desc(), h.readDB.GamePassPackages.ID.Asc()).Find()
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error()))
			return
		}

		res.Packages = make([]packageItem, len(packages))
		for i, p := range packages {
			res.Packages[i] = packageItem{
				ID:            p.ID,
				Name:          p.Name,
				PassCount:     p.PassCount,
				Price:         p.Price,
				OriginalPrice: p.OriginalPrice,
				ValidDays:     p.ValidDays,
				ActivityID:    p.ActivityID,
			}
		}

		ctx.Payload(res)
	}
}

// ==================== 购买套餐 API ====================

type purchasePackageRequest struct {
	PackageID int64   `json:"package_id" binding:"required"`
	Count     int32   `json:"count"`      // 购买数量
	CouponIDs []int64 `json:"coupon_ids"` // 优惠券ID列表
}

type purchasePackageResponse struct {
	OrderNo string `json:"order_no"`
	Message string `json:"message"`
}

// PurchaseGamePassPackage 购买次数卡套餐（创建订单）
// @Summary 购买次数卡套餐
// @Description 购买次数卡套餐，创建订单等待支付，支持使用优惠券
// @Tags APP端.用户
// @Accept json
// @Produce json
// @Security LoginVerifyToken
// @Param RequestBody body purchasePackageRequest true "请求参数"
// @Success 200 {object} purchasePackageResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/game-passes/purchase [post]
func (h *handler) PurchaseGamePassPackage() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(purchasePackageRequest)
		res := new(purchasePackageResponse)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		if req.Count <= 0 {
			req.Count = 1
		}

		userID := int64(ctx.SessionUserInfo().Id)

		// 查询套餐信息
		pkg, err := h.readDB.GamePassPackages.WithContext(ctx.RequestContext()).
			Where(h.readDB.GamePassPackages.ID.Eq(req.PackageID)).
			Where(h.readDB.GamePassPackages.Status.Eq(1)).
			First()
		if err != nil || pkg == nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "套餐不存在或已下架"))
			return
		}

		// Calculate total price
		totalPrice := pkg.Price * int64(req.Count)

		// 创建订单 (支持优惠券)
		now := time.Now()
		orderNo := now.Format("20060102150405") + fmt.Sprintf("%04d", now.UnixNano()%10000)
		order := &model.Orders{
			UserID:       userID,
			OrderNo:      "GP" + orderNo,
			SourceType:   4, // 次数卡购买
			TotalAmount:  totalPrice,
			ActualAmount: totalPrice,
			Status:       1, // 待支付
			Remark:       fmt.Sprintf("game_pass_package:%s|pkg_id:%d|count:%d", pkg.Name, pkg.ID, req.Count),
			CreatedAt:    now,
			UpdatedAt:    now,
		}

		// 应用优惠券 (如果有)
		var appliedCouponVal int64
		var couponID int64

		if len(req.CouponIDs) > 0 {
			couponID = req.CouponIDs[0]
			// 调用优惠券应用函数
			applied, err := h.applyCouponToGamePassOrder(ctx, order, userID, couponID)
			if err != nil {
				ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error()))
				return
			}
			appliedCouponVal = applied

			// 记录优惠券到订单
			if appliedCouponVal > 0 {
				order.CouponID = couponID
				order.Remark += fmt.Sprintf("|coupon:%d", couponID)
			}
		}

		// 保存订单
		if err := h.writeDB.Orders.WithContext(ctx.RequestContext()).
			Omit(h.writeDB.Orders.PaidAt, h.writeDB.Orders.CancelledAt).
			Create(order); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, err.Error()))
			return
		}

		// 如果使用了优惠券,记录到order_coupons表
		if appliedCouponVal > 0 {
			_ = h.writeDB.OrderCoupons.WithContext(ctx.RequestContext()).UnderlyingDB().Exec(
				"INSERT IGNORE INTO order_coupons (order_id, user_coupon_id, applied_amount, created_at) VALUES (?,?,?,NOW(3))",
				order.ID, couponID, appliedCouponVal)
		}

		// 处理0元订单
		if order.ActualAmount == 0 {
			order.Status = 2
			order.PaidAt = now
			h.writeDB.Orders.WithContext(ctx.RequestContext()).
				Where(h.writeDB.Orders.OrderNo.Eq(orderNo)).
				Updates(map[string]any{
					h.writeDB.Orders.Status.ColumnName().String(): 2,
					h.writeDB.Orders.PaidAt.ColumnName().String(): now,
				})

			// 0元订单确认优惠券扣减
			if appliedCouponVal > 0 {
				h.confirmCouponUsage(ctx, couponID, order.ID, now)
			}
		}

		res.OrderNo = order.OrderNo
		res.Message = "订单创建成功，请完成支付"
		ctx.Payload(res)
	}
}

// ==================== 优惠券辅助函数 ====================

// applyCouponToGamePassOrder 应用优惠券到次卡购买订单
// 返回应用的优惠金额 (分)
func (h *handler) applyCouponToGamePassOrder(ctx core.Context, order *model.Orders, userID int64, userCouponID int64) (int64, error) {
	// 使用 SELECT ... FOR UPDATE 锁定行
	uc, _ := h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).
		Clauses(clause.Locking{Strength: "UPDATE"}).
		Where(
			h.writeDB.UserCoupons.ID.Eq(userCouponID),
			h.writeDB.UserCoupons.UserID.Eq(userID),
		).First()

	if uc == nil {
		return 0, nil
	}

	// 检查状态 (必须是可用状态)
	if uc.Status != 1 {
		return 0, fmt.Errorf("优惠券不可用")
	}

	// 获取优惠券模板
	sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).
		Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID), h.readDB.SystemCoupons.Status.Eq(1)).
		First()

	now := time.Now()
	if sc == nil {
		return 0, fmt.Errorf("优惠券模板不存在")
	}

	// 验证有效期
	if uc.ValidStart.After(now) {
		return 0, fmt.Errorf("优惠券未到开始时间")
	}
	if !uc.ValidEnd.IsZero() && uc.ValidEnd.Before(now) {
		return 0, fmt.Errorf("优惠券已过期")
	}

	// 验证使用范围 (次卡购买只支持全场券 scope_type=1)
	if sc.ScopeType != 1 {
		return 0, fmt.Errorf("次卡购买仅支持全场通用优惠券")
	}

	// 验证门槛
	if order.TotalAmount < sc.MinSpend {
		return 0, fmt.Errorf("未达优惠券使用门槛")
	}

	// 50% 封顶
	cap := order.TotalAmount / 2
	remainingCap := cap - order.DiscountAmount
	if remainingCap <= 0 {
		return 0, fmt.Errorf("已达优惠封顶限制")
	}

	// 计算优惠金额
	applied := int64(0)
	switch sc.DiscountType {
	case 1: // 金额券
		bal := uc.BalanceAmount
		if bal > 0 {
			if bal > remainingCap {
				applied = remainingCap
			} else {
				applied = bal
			}
		}
	case 2: // 满减券
		applied = sc.DiscountValue
		if applied > remainingCap {
			applied = remainingCap
		}
	case 3: // 折扣券
		rate := sc.DiscountValue
		if rate < 0 {
			rate = 0
		}
		if rate > 1000 {
			rate = 1000
		}
		newAmt := order.ActualAmount * rate / 1000
		d := order.ActualAmount - newAmt
		if d > remainingCap {
			applied = remainingCap
		} else {
			applied = d
		}
	}

	if applied > order.ActualAmount {
		applied = order.ActualAmount
	}
	if applied <= 0 {
		return 0, nil
	}

	// 更新订单金额
	order.ActualAmount -= applied
	order.DiscountAmount += applied

	// 扣减优惠券余额或标记为冻结
	if sc.DiscountType == 1 {
		// 金额券：直接扣余额
		newBal := uc.BalanceAmount - applied
		newStatus := int32(1)
		if newBal <= 0 {
			newBal = 0
			newStatus = 2 // 已使用
		}
		res := h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).UnderlyingDB().Exec(
			"UPDATE user_coupons SET balance_amount = ?, status = ? WHERE id = ?",
			newBal, newStatus, userCouponID)
		if res.Error != nil {
			return 0, res.Error
		}

		// 记录流水
		_ = h.writeDB.UserCouponLedger.WithContext(ctx.RequestContext()).Create(&model.UserCouponLedger{
			UserID:       userID,
			UserCouponID: userCouponID,
			ChangeAmount: -applied,
			BalanceAfter: newBal,
			OrderID:      order.ID,
			Action:       "reserve", // 预扣
			CreatedAt:    time.Now(),
		})
	} else {
		// 满减/折扣券：标记为冻结 (状态4)
		res := h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).UnderlyingDB().Exec(
			"UPDATE user_coupons SET status = 4 WHERE id = ? AND status = 1", userCouponID)
		if res.Error != nil {
			return 0, res.Error
		}
		if res.RowsAffected == 0 {
			return 0, fmt.Errorf("优惠券已被使用")
		}

		// 记录流水
		_ = h.writeDB.UserCouponLedger.WithContext(ctx.RequestContext()).Create(&model.UserCouponLedger{
			UserID:       userID,
			UserCouponID: userCouponID,
			ChangeAmount: 0,
			BalanceAfter: 0,
			OrderID:      order.ID,
			Action:       "reserve",
			CreatedAt:    time.Now(),
		})
	}

	return applied, nil
}

// confirmCouponUsage 确认优惠券使用 (支付成功后调用)
func (h *handler) confirmCouponUsage(ctx core.Context, userCouponID int64, orderID int64, paidAt time.Time) {
	uc, _ := h.readDB.UserCoupons.WithContext(ctx.RequestContext()).
		Where(h.readDB.UserCoupons.ID.Eq(userCouponID)).
		First()
	if uc == nil {
		return
	}

	sc, _ := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).
		Where(h.readDB.SystemCoupons.ID.Eq(uc.CouponID)).
		First()
	if sc == nil {
		return
	}

	// 金额券：余额已经扣减,只需记录used_order_id
	if sc.DiscountType == 1 {
		_, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).
			Where(h.writeDB.UserCoupons.ID.Eq(userCouponID)).
			Updates(map[string]any{
				h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): orderID,
				h.writeDB.UserCoupons.UsedAt.ColumnName().String():      paidAt,
			})
	} else {
		// 满减/折扣券：状态从冻结(4)改为已使用(2)
		_, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).
			Where(h.writeDB.UserCoupons.ID.Eq(userCouponID)).
			Updates(map[string]any{
				h.writeDB.UserCoupons.Status.ColumnName().String():      2,
				h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): orderID,
				h.writeDB.UserCoupons.UsedAt.ColumnName().String():      paidAt,
			})
	}
}
