package app

import (
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/util/remark"
	"bindbox-game/internal/repository/mysql/model"
	"fmt"
	"time"
)

// couponJoinResult 优惠券联合查询结果
type couponJoinResult struct {
	// UserCoupon fields
	UserCouponID  int64     `gorm:"column:uc_id"`
	CouponID      int64     `gorm:"column:coupon_id"`
	ValidStart    time.Time `gorm:"column:valid_start"`
	ValidEnd      time.Time `gorm:"column:valid_end"`
	BalanceAmount int64     `gorm:"column:balance_amount"`
	// SystemCoupon fields
	ScopeType     int32 `gorm:"column:scope_type"`
	ActivityID    int64 `gorm:"column:sc_activity_id"`
	MinSpend      int64 `gorm:"column:min_spend"`
	DiscountType  int32 `gorm:"column:discount_type"`
	DiscountValue int64 `gorm:"column:discount_value"`
}

// applyCouponWithCap 优惠券抵扣（含50%封顶与金额券部分使用）
// 功能：在订单上应用一张用户券，实施总价50%封顶；金额券支持"部分使用"，在 remark 记录明细
// 参数：
//   - ctx：请求上下文
//   - userID：用户ID
//   - order：待更新的订单对象（入参引用，被本函数更新 discount_amount/actual_amount/remark）
//   - activityID：活动ID用于范围校验
//   - userCouponID：用户持券ID
//
// 返回：本次实际应用的抵扣金额（分）；若不适用或受封顶为0则返回0
func (h *handler) applyCouponWithCap(ctx core.Context, userID int64, order *model.Orders, activityID int64, userCouponID int64) int64 {
	// 使用单次 JOIN 查询替代 3 次分离查询，减少数据库往返
	var result couponJoinResult
	err := h.repo.GetDbR().Raw(`
		SELECT 
			uc.id as uc_id,
			uc.coupon_id,
			uc.valid_start,
			uc.valid_end,
			COALESCE(uc.balance_amount, 0) as balance_amount,
			sc.scope_type,
			sc.activity_id as sc_activity_id,
			sc.min_spend,
			sc.discount_type,
			sc.discount_value
		FROM user_coupons uc
		INNER JOIN system_coupons sc ON uc.coupon_id = sc.id AND sc.status = 1
		WHERE uc.id = ? AND uc.user_id = ? AND uc.status IN (1, 4)
		LIMIT 1
	`, userCouponID, userID).Scan(&result).Error

	if err != nil || result.UserCouponID == 0 {
		return 0
	}

	now := time.Now()
	if result.ValidStart.After(now) {
		return 0
	}
	if !result.ValidEnd.IsZero() && result.ValidEnd.Before(now) {
		return 0
	}
	scopeOK := (result.ScopeType == 1) || (result.ScopeType == 2 && result.ActivityID == activityID)
	if !scopeOK {
		return 0
	}
	if order.TotalAmount < result.MinSpend {
		return 0
	}
	cap := order.TotalAmount / 2
	remainingCap := cap - order.DiscountAmount
	if remainingCap <= 0 {
		return 0
	}
	applied := int64(0)
	switch result.DiscountType {
	case 1: // 金额券
		bal := result.BalanceAmount
		if bal > 0 {
			if bal > remainingCap {
				applied = remainingCap
			} else {
				applied = bal
			}
		}
	case 2: // 满减券
		applied = result.DiscountValue
		if applied > remainingCap {
			applied = remainingCap
		}
	case 3: // 折扣券
		rate := result.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
	}
	order.DiscountAmount += applied
	order.ActualAmount -= applied
	order.Remark = order.Remark + fmt.Sprintf("|c:%d:%d", userCouponID, applied)
	return applied
}

// preDeductCouponInTx 在事务中预扣优惠券余额
// 功能：原子性地扣减余额并设置 status=4（预扣中），防止并发超额使用
// 参数：
//   - ctx：请求上下文
//   - tx：数据库事务（必须在事务中调用）
//   - userID：用户ID
//   - userCouponID：用户持券ID
//   - appliedAmount：要预扣的金额（分）
//   - orderID：关联的订单ID
//
// 返回：是否成功预扣
func (h *handler) preDeductCouponInTx(ctx core.Context, txDB interface {
	Exec(sql string, values ...interface{}) interface {
		RowsAffected() int64
		Error() error
	}
}, userID int64, userCouponID int64, appliedAmount int64, orderID int64) bool {
	if appliedAmount <= 0 || userCouponID <= 0 {
		return false
	}
	now := time.Now()

	// 原子更新：扣减余额 + 设置状态为预扣中(4) + 关联订单
	// 条件：余额足够 且 状态为未使用(1)或使用中(4，支持同一券多订单分批扣减场景，但需余额足够)
	result := txDB.Exec(`
		UPDATE user_coupons 
		SET balance_amount = balance_amount - ?,
			status = CASE WHEN balance_amount - ? <= 0 THEN 4 ELSE 4 END,
			used_order_id = ?,
			used_at = ?
		WHERE id = ? AND user_id = ? AND balance_amount >= ? AND status IN (1, 4)
	`, appliedAmount, appliedAmount, orderID, now, userCouponID, userID, appliedAmount)

	if result.Error() != nil {
		return false
	}

	return result.RowsAffected() > 0
}

// updateUserCouponAfterApply 应用后更新用户券（扣减余额或核销）
// 功能：根据订单 remark 中记录的 applied_amount，
//
//	对直金额券扣减余额并在余额为0时核销；满减/折扣券一次性核销
//
// 参数：
//   - ctx：请求上下文
//   - userID：用户ID
//   - order：订单（用于读取 remark 和写入 used_order_id）
//   - userCouponID：用户持券ID
//
// 返回：无
func (h *handler) updateUserCouponAfterApply(ctx core.Context, userID int64, order *model.Orders, userCouponID int64) {
	// 使用单次 JOIN 查询替代 3 次分离查询
	var result couponJoinResult
	err := h.repo.GetDbR().Raw(`
		SELECT 
			uc.id as uc_id,
			uc.coupon_id,
			uc.valid_start,
			uc.valid_end,
			COALESCE(uc.balance_amount, 0) as balance_amount,
			sc.scope_type,
			sc.activity_id as sc_activity_id,
			sc.min_spend,
			sc.discount_type,
			sc.discount_value
		FROM user_coupons uc
		INNER JOIN system_coupons sc ON uc.coupon_id = sc.id AND sc.status = 1
		WHERE uc.id = ? AND uc.user_id = ? AND uc.status IN (1, 4)
		LIMIT 1
	`, userCouponID, userID).Scan(&result).Error

	if err != nil || result.UserCouponID == 0 {
		return
	}

	// 从 remark 中解析 applied amount
	applied := int64(0)
	remark := order.Remark
	p := 0
	for i := 0; i < len(remark); i++ {
		if remark[i] == '|' {
			seg := remark[p:i]
			if len(seg) > 2 && seg[:2] == "c:" {
				j := 2
				var id int64
				for j < len(seg) && seg[j] >= '0' && seg[j] <= '9' {
					id = id*10 + int64(seg[j]-'0')
					j++
				}
				if j < len(seg) && seg[j] == ':' {
					j++
					var amt int64
					for j < len(seg) && seg[j] >= '0' && seg[j] <= '9' {
						amt = amt*10 + int64(seg[j]-'0')
						j++
					}
					if id == userCouponID {
						applied = amt
					}
				}
			}
			p = i + 1
		}
	}

	if result.DiscountType == 1 { // 金额券
		newBal := result.BalanceAmount - applied
		if newBal < 0 {
			newBal = 0
		}
		if newBal == 0 {
			_, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{"balance_amount": newBal, h.writeDB.UserCoupons.Status.ColumnName().String(): 2, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()})
		} else {
			_, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{"balance_amount": newBal, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()})
		}
	} else { // 满减/折扣券
		_, _ = h.writeDB.UserCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.UserCoupons.ID.Eq(userCouponID), h.writeDB.UserCoupons.UserID.Eq(userID)).Updates(map[string]any{h.writeDB.UserCoupons.Status.ColumnName().String(): 2, h.writeDB.UserCoupons.UsedOrderID.ColumnName().String(): order.ID, h.writeDB.UserCoupons.UsedAt.ColumnName().String(): time.Now()})
	}
}

// markOrderPaid 将订单标记为已支付
// 功能：用于0元订单直接置为已支付并写入支付时间
// 参数：
//   - ctx：请求上下文
//   - orderNo：订单号
//
// 返回：无
func (h *handler) markOrderPaid(ctx core.Context, orderNo string) {
	now := time.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})
}

func (h *handler) randomID(prefix string) string {
	now := time.Now()
	// 增加随机数防止同1秒内并发生成相同的ID
	return fmt.Sprintf("%s%s%03d", prefix, now.Format("20060102150405"), time.Now().UnixNano()%1000)
}

func (h *handler) orderModel(userID int64, orderNo string, amount int64, activityID int64, issueID int64, count int64) *model.Orders {
	return &model.Orders{UserID: userID, OrderNo: orderNo, SourceType: 2, TotalAmount: amount, DiscountAmount: 0, PointsAmount: 0, ActualAmount: amount, Status: 1, IsConsumed: 0, Remark: fmt.Sprintf("lottery:activity:%d|issue:%d|count:%d", activityID, issueID, count)}
}

func parseSlotFromRemark(remarkStr string) int64 {
	rmk := remark.Parse(remarkStr)
	if len(rmk.Slots) > 0 {
		return rmk.Slots[0].SlotIndex
	}
	return -1
}

func parseItemCardIDFromRemark(remarkStr string) int64 {
	return remark.Parse(remarkStr).ItemCardID
}

func (h *handler) joinSigPayload(userID int64, issueID int64, ts int64, nonce int64) string {
	return fmt.Sprintf("%d|%d|%d|%d", userID, issueID, ts, nonce)
}

func buildSlotsRemarkWithScalarCount(slots []int64) string {
	s := ""
	for i := range slots {
		if i > 0 {
			s += ","
		}
		s += fmt.Sprintf("%d:%d", slots[i]-1, 1)
	}
	return s
}

func parseSlotsCountsFromRemark(remarkStr string) ([]int64, []int64) {
	rmk := remark.Parse(remarkStr)
	idxs := make([]int64, 0, len(rmk.Slots))
	cnts := make([]int64, 0, len(rmk.Slots))
	for _, s := range rmk.Slots {
		idxs = append(idxs, s.SlotIndex+1) // 补偿前端展示逻辑，保持原样
		cnts = append(cnts, s.Count)
	}
	return idxs, cnts
}

func parseIssueIDFromRemark(remarkStr string) int64 {
	return remark.Parse(remarkStr).IssueID
}

func parseCountFromRemark(remarkStr string) int64 {
	return remark.Parse(remarkStr).Count
}

// shouldTriggerInstantDraw 判断是否应该触发即时开奖
// 功能：封装即时开奖触发条件判断，避免条件重复
// 参数：
//   - orderStatus：订单状态（2=已支付）
//   - drawMode：开奖模式（"instant"=即时开奖）
//
// 返回：是否应该触发即时开奖
func shouldTriggerInstantDraw(orderStatus int32, drawMode string) bool {
	return orderStatus == 2 && drawMode == "instant"
}
