package admin

import (
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"
	"strconv"
	"time"

	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	paypkg "bindbox-game/internal/pkg/pay"
	"bindbox-game/internal/pkg/validation"
	"bindbox-game/internal/repository/mysql/model"
	usersvc "bindbox-game/internal/service/user"
)

type createRefundRequest struct {
	OrderNo string `json:"order_no" form:"order_no"`
	Amount  int64  `json:"amount" form:"amount"`
	Reason  string `json:"reason" form:"reason"`
}

type createRefundResponse struct {
	OrderNo string `json:"order_no"`
	Status  string `json:"status"`
}

// CreateRefund 管理端创建退款(占位实现)
// 入参：order_no、amount(分)、reason
// 行为：将订单状态置为已退款，并写入积分流水refund_restore(按订单积分抵扣金额恢复)
// 注意：真实场景需调用微信退款API并根据结果更新状态，本版本为占位保证编译与联调
func (h *handler) CreateRefund() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(createRefundRequest)
		rsp := new(createRefundResponse)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}
		// 查询订单
		order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(req.OrderNo)).First()
		if err != nil || order == nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 160001, "order not found"))
			return
		}

		// 检查订单状态，只有已支付的订单才能退款
		if order.Status != 2 {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 160010, "订单状态不允许退款"))
			return
		}

		// 预检查：检查是否有已兑换积分的资产
		allInvs, _ := h.readDB.UserInventory.WithContext(ctx.RequestContext()).Where(h.readDB.UserInventory.OrderID.Eq(order.ID)).Where(h.readDB.UserInventory.Status.In(1, 3)).Find()
		rePoints := regexp.MustCompile(`\|redeemed_points=(\d+)`)

		var refundedSumCents int64
		var isFullRefund bool

		// ⭐ 根据 ActualAmount 决定是否需要微信退款
		if order.ActualAmount == 0 {
			// ActualAmount=0：无需微信退款，直接标记为全额退款
			isFullRefund = true
			h.logger.Info(fmt.Sprintf("refund: ActualAmount=0, skip wechat refund: order=%s", order.OrderNo))
		} else {
			// ActualAmount>0：需要调用微信退款
			// 计算已退款与可退余额（分）
			ledgers, _ := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(order.OrderNo)).Find()
			for _, l := range ledgers {
				if l.Action == "refund_amount" {
					refundedSumCents += l.Points * 100
				}
			}
			refundable := order.ActualAmount - refundedSumCents
			if refundable < 0 {
				refundable = 0
			}
			if req.Amount <= 0 || req.Amount > refundable {
				ctx.AbortWithError(core.Error(http.StatusBadRequest, 160005, fmt.Sprintf("invalid refund amount, max=%d", refundable)))
				return
			}

			// 调用微信真实退款
			wc, err := paypkg.NewWechatPayClient(ctx.RequestContext())
			if err != nil {
				ctx.AbortWithError(core.Error(http.StatusBadRequest, 160006, err.Error()))
				return
			}
			refundNo := fmt.Sprintf("R%s-%d", order.OrderNo, time.Now().Unix())
			refundID, status, err := wc.RefundOrder(ctx.RequestContext(), order.OrderNo, refundNo, req.Amount, order.ActualAmount, req.Reason)
			if err != nil {
				ctx.AbortWithError(core.Error(http.StatusBadRequest, 160007, err.Error()))
				return
			}
			pr := &model.PaymentRefunds{
				OrderID:      order.ID,
				OrderNo:      order.OrderNo,
				RefundNo:     refundNo,
				Channel:      "wechat_jsapi",
				Status:       status,
				AmountRefund: req.Amount,
				Reason:       req.Reason,
				SuccessTime:  time.Now(),
				Raw: func() string {
					b, _ := json.Marshal(map[string]any{"refund_id": refundID, "status": status})
					return string(b)
				}(),
			}
			if err := h.writeDB.PaymentRefunds.WithContext(ctx.RequestContext()).Create(pr); err != nil {
				ctx.AbortWithError(core.Error(http.StatusInternalServerError, 160009, err.Error()))
				return
			}

			// 判断是否为全额退款
			isFullRefund = req.Amount == order.ActualAmount-refundedSumCents
		}

		// 更新订单状态为已退款（全额退款时）
		if isFullRefund {
			_, err = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.writeDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{
				h.writeDB.Orders.Status.ColumnName().String():    4,
				h.writeDB.Orders.UpdatedAt.ColumnName().String(): time.Now(),
			})
			if err != nil {
				ctx.AbortWithError(core.Error(http.StatusInternalServerError, 160002, err.Error()))
				return
			}
			// 全额退款：恢复订单中使用的优惠券（细化余额与状态逻辑）
			type ocRow struct {
				UserCouponID  int64
				AppliedAmount int64
				DiscountType  int32
				DiscountValue int64
				BalanceAmount int64
			}
			var rows []ocRow
			_ = h.repo.GetDbR().Raw("SELECT oc.user_coupon_id, oc.applied_amount, sc.discount_type, sc.discount_value, uc.balance_amount FROM order_coupons oc JOIN user_coupons uc ON uc.id=oc.user_coupon_id JOIN system_coupons sc ON sc.id=uc.coupon_id WHERE oc.order_id=?", order.ID).Scan(&rows).Error
			for _, r := range rows {
				if r.UserCouponID > 0 && r.AppliedAmount > 0 {
					newBal := r.BalanceAmount + r.AppliedAmount
					if r.DiscountType == 1 { // 直金额券：判断回退后是否全满
						if newBal >= r.DiscountValue {
							_ = h.repo.GetDbW().Exec("UPDATE user_coupons SET balance_amount=?, status=1, used_order_id=0, used_at=NULL WHERE id=?", r.DiscountValue, r.UserCouponID).Error
							newBal = r.DiscountValue
						} else {
							// 若金额未满，维持 status=2 (已使用/使用中)
							_ = h.repo.GetDbW().Exec("UPDATE user_coupons SET balance_amount=?, status=2 WHERE id=?", newBal, r.UserCouponID).Error
						}
					} else { // 满减/折扣券：一律恢复为未使用
						_ = h.repo.GetDbW().Exec("UPDATE user_coupons SET status=1, used_order_id=0, used_at=NULL WHERE id=?", r.UserCouponID).Error
						newBal = 0
					}
					// 记录流水
					ledger := &model.UserCouponLedger{
						UserID:       order.UserID,
						UserCouponID: r.UserCouponID,
						ChangeAmount: r.AppliedAmount,
						BalanceAfter: newBal,
						OrderID:      order.ID,
						Action:       "refund_restore",
						CreatedAt:    time.Now(),
					}
					_ = h.repo.GetDbW().Create(ledger).Error
				}
			}

			// 全额退款：回收中奖资产与奖品库存（包含已兑换积分的资产）
			svc := usersvc.New(h.logger, h.repo)
			// 直接使用已初始化的 activity service 清理格位
			_ = h.activity.ClearIchibanPositionsByOrderID(ctx.RequestContext(), order.ID)

			var pointsShortage bool
			for _, inv := range allInvs {
				if inv.Status == 1 {
					// 状态1（持有）：更新状态为2 (作废)
					_ = h.repo.GetDbW().Exec("UPDATE user_inventory SET status=2, updated_at=NOW(3), remark=CONCAT(IFNULL(remark,''),'|refund_void') WHERE id=?", inv.ID).Error
					// 恢复奖品库存
					if inv.RewardID > 0 {
						_ = h.repo.GetDbW().Exec("UPDATE activity_reward_settings SET quantity = quantity + 1 WHERE id=?", inv.RewardID).Error
					}
				} else if inv.Status == 3 {
					// 状态3（已兑换）：扣除积分并作废
					matches := rePoints.FindStringSubmatch(inv.Remark)
					if len(matches) > 1 {
						p, _ := strconv.ParseInt(matches[1], 10, 64)
						if p > 0 {
							// 扣除积分（记录流水）- 使用柔性扣减
							_, consumed, err := svc.ConsumePointsForRefund(ctx.RequestContext(), order.UserID, p, "user_inventory", strconv.FormatInt(inv.ID, 10), "订单退款回收兑换积分")
							if err != nil {
								h.logger.Error(fmt.Sprintf("refund reclaim points failed: order=%s user=%d points=%d err=%v", order.OrderNo, order.UserID, p, err))
							}
							if consumed < p {
								pointsShortage = true
							}
						}
					}
					// 更新状态为2 (作废)
					_ = h.repo.GetDbW().Exec("UPDATE user_inventory SET status=2, updated_at=NOW(3), remark=CONCAT(IFNULL(remark,''),'|refund_reclaimed') WHERE id=?", inv.ID).Error
					// 恢复奖品库存
					if inv.RewardID > 0 {
						_ = h.repo.GetDbW().Exec("UPDATE activity_reward_settings SET quantity = quantity + 1 WHERE id=?", inv.RewardID).Error
					}
				}
			}

			// 如果扣减积分不足，将用户列入黑名单
			if pointsShortage {
				h.logger.Warn(fmt.Sprintf("[退款黑名单] 用户积分不足以回收已兑换资产，将其列入黑名单: user_id=%d, order_no=%s", order.UserID, order.OrderNo))
				_ = h.repo.GetDbW().Exec("UPDATE users SET status=? WHERE id=?", model.UserStatusBlacklist, order.UserID).Error
			}

			// 全额退款：取消待发货记录
			_ = h.repo.GetDbW().Exec("UPDATE shipping_records SET status=4, updated_at=NOW(3), remark=CONCAT(IFNULL(remark,''),'|refund_cancel') WHERE order_id=? AND status=1", order.ID).Error

			// 全额退款：回退道具卡（支持两种记录方式）
			var itemCardIDs []int64
			// 方式1：从 activity_draw_effects 表查询（无限赏等游戏类型）
			_ = h.repo.GetDbR().Raw("SELECT user_item_card_id FROM activity_draw_effects WHERE draw_log_id IN (SELECT id FROM activity_draw_logs WHERE order_id=?)", order.ID).Scan(&itemCardIDs).Error
			// 方式2：从 user_item_cards 表的 used_draw_log_id 直接查询（对对碰等游戏类型）
			var itemCardIDsFromItemCards []int64
			_ = h.repo.GetDbR().Raw("SELECT id FROM user_item_cards WHERE used_draw_log_id IN (SELECT id FROM activity_draw_logs WHERE order_id=?) AND status=2", order.ID).Scan(&itemCardIDsFromItemCards).Error
			// 合并去重
			idSet := make(map[int64]struct{})
			for _, icID := range itemCardIDs {
				if icID > 0 {
					idSet[icID] = struct{}{}
				}
			}
			for _, icID := range itemCardIDsFromItemCards {
				if icID > 0 {
					idSet[icID] = struct{}{}
				}
			}
			// 执行退还
			for icID := range idSet {
				_ = h.repo.GetDbW().Exec("UPDATE user_item_cards SET status=1, used_at=NULL, used_draw_log_id=0, used_activity_id=0, used_issue_id=0, updated_at=NOW(3) WHERE id=?", icID).Error
			}

			// 全额退款：回退次数卡（user_game_passes）
			// 优先解析新格式: gp_use:ID:Count (支持多张卡、多数量)
			reGpNew := regexp.MustCompile(`gp_use:(\d+):(\d+)`)
			matchesNew := reGpNew.FindAllStringSubmatch(order.Remark, -1)
			if len(matchesNew) > 0 {
				for _, m := range matchesNew {
					gpID, _ := strconv.ParseInt(m[1], 10, 64)
					gpCount, _ := strconv.ParseInt(m[2], 10, 64)
					if gpID > 0 && gpCount > 0 {
						if err := h.repo.GetDbW().Exec("UPDATE user_game_passes SET remaining = remaining + ?, total_used = GREATEST(total_used - ?, 0), updated_at = NOW(3) WHERE id = ?", gpCount, gpCount, gpID).Error; err != nil {
							h.logger.Error(fmt.Sprintf("refund restore game_pass failed: order=%s gp_id=%d count=%d err=%v", order.OrderNo, gpID, gpCount, err))
						} else {
							h.logger.Info(fmt.Sprintf("refund restore game_pass success: order=%s gp_id=%d count=%d", order.OrderNo, gpID, gpCount))
						}
					}
				}
			} else {
				// 兼容旧格式: game_pass:ID (仅恢复 1 次)
				reGamePass := regexp.MustCompile(`game_pass:(\d+)`)
				gamePassMatches := reGamePass.FindStringSubmatch(order.Remark)
				if len(gamePassMatches) > 1 {
					gamePassID, _ := strconv.ParseInt(gamePassMatches[1], 10, 64)
					if gamePassID > 0 {
						if err := h.repo.GetDbW().Exec("UPDATE user_game_passes SET remaining = remaining + 1, total_used = GREATEST(total_used - 1, 0), updated_at = NOW(3) WHERE id = ?", gamePassID).Error; err != nil {
							h.logger.Error(fmt.Sprintf("refund restore game_pass failed: order=%s game_pass_id=%d err=%v", order.OrderNo, gamePassID, err))
						} else {
							h.logger.Info(fmt.Sprintf("refund restore game_pass success: order=%s game_pass_id=%d", order.OrderNo, gamePassID))
						}
					}
				}
			}
		}
		// 记录积分按比例恢复（幂等增量）- 仅对 ActualAmount > 0 的订单执行
		if order.PointsAmount > 0 && order.ActualAmount > 0 {
			restores, _ := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(
				h.readDB.UserPointsLedger.RefTable.Eq("orders"),
				h.readDB.UserPointsLedger.RefID.Eq(order.OrderNo),
				h.readDB.UserPointsLedger.Action.Eq("refund_restore"),
			).Find()
			var restoredPointsSum int64
			for _, r := range restores {
				restoredPointsSum += r.Points
			}
			totalRefundedAfter := refundedSumCents + req.Amount
			if totalRefundedAfter > order.ActualAmount {
				totalRefundedAfter = order.ActualAmount
			}
			restoreCentsTarget := (order.PointsAmount * totalRefundedAfter) / order.ActualAmount
			restorePointsTarget := restoreCentsTarget
			toAdd := restorePointsTarget - restoredPointsSum
			maxPoints := order.PointsAmount
			if toAdd < 0 {
				toAdd = 0
			}
			if toAdd > (maxPoints - restoredPointsSum) {
				toAdd = maxPoints - restoredPointsSum
			}
			if toAdd > 0 {
				ledger := &model.UserPointsLedger{
					UserID:   order.UserID,
					Action:   "refund_restore",
					Points:   toAdd,
					RefTable: "orders",
					RefID:    order.OrderNo,
					Remark:   req.Reason,
				}
				if err := h.writeDB.UserPointsLedger.WithContext(ctx.RequestContext()).Create(ledger); err != nil {
					ctx.AbortWithError(core.Error(http.StatusInternalServerError, 160003, err.Error()))
					return
				}
			}
		}
		rsp.OrderNo = order.OrderNo
		rsp.Status = "success"
		ctx.Payload(rsp)
	}
}

type listRefundsRequest struct {
	OrderNo string `form:"order_no"`
	Status  string `form:"status"`
	Page    int    `form:"page"`
	Size    int    `form:"size"`
}

type listRefundsResponse struct {
	Total int64            `json:"total"`
	List  []map[string]any `json:"list"`
}

func (h *handler) ListRefunds() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(listRefundsRequest)
		if err := ctx.ShouldBindQuery(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}
		if req.Page <= 0 {
			req.Page = 1
		}
		if req.Size <= 0 || req.Size > 100 {
			req.Size = 20
		}
		q := h.readDB.PaymentRefunds.WithContext(ctx.RequestContext()).ReadDB()
		if req.OrderNo != "" {
			q = q.Where(h.readDB.PaymentRefunds.OrderNo.Eq(req.OrderNo))
		}
		if req.Status != "" {
			q = q.Where(h.readDB.PaymentRefunds.Status.Eq(req.Status))
		}
		total, _ := q.Count()
		items, _ := q.Order(h.readDB.PaymentRefunds.ID.Desc()).Limit(req.Size).Offset((req.Page - 1) * req.Size).Find()
		var list []map[string]any
		for _, it := range items {
			list = append(list, map[string]any{
				"refund_no":     it.RefundNo,
				"order_no":      it.OrderNo,
				"status":        it.Status,
				"channel":       it.Channel,
				"amount_refund": it.AmountRefund,
				"reason":        it.Reason,
				"created_at":    it.CreatedAt,
			})
		}
		ctx.Payload(&listRefundsResponse{Total: total, List: list})
	}
}

type getRefundDetailResponse struct {
	Refund map[string]any `json:"refund"`
	Order  map[string]any `json:"order"`
}

func (h *handler) GetRefundDetail() core.HandlerFunc {
	return func(ctx core.Context) {
		refundNo := ctx.Param("refund_no")
		if refundNo == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "missing refund_no"))
			return
		}
		r, err := h.readDB.PaymentRefunds.WithContext(ctx.RequestContext()).Where(h.readDB.PaymentRefunds.RefundNo.Eq(refundNo)).First()
		if err != nil || r == nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 160008, "refund not found"))
			return
		}
		o, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(r.OrderNo)).First()
		resp := &getRefundDetailResponse{
			Refund: map[string]any{
				"refund_no":     r.RefundNo,
				"order_no":      r.OrderNo,
				"status":        r.Status,
				"channel":       r.Channel,
				"amount_refund": r.AmountRefund,
				"reason":        r.Reason,
				"created_at":    r.CreatedAt,
			},
			Order: map[string]any{
				"order_no": func() string {
					if o != nil {
						return o.OrderNo
					}
					return ""
				}(),
				"user_id": func() int64 {
					if o != nil {
						return o.UserID
					}
					return 0
				}(),
				"status": func() int32 {
					if o != nil {
						return o.Status
					}
					return 0
				}(),
				"actual_amount": func() int64 {
					if o != nil {
						return o.ActualAmount
					}
					return 0
				}(),
				"points_amount": func() int64 {
					if o != nil {
						return o.PointsAmount
					}
					return 0
				}(),
			},
		}
		ctx.Payload(resp)
	}
}
