package admin

import (
	"encoding/json"
	"fmt"
	"net/http"
	"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"
)

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
		}
		// 计算已退款与可退余额（分）
		ledgers, _ := h.readDB.UserPointsLedger.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.UserPointsLedger.RefTable.Eq("orders"), h.readDB.UserPointsLedger.RefID.Eq(order.OrderNo)).Find()
		var refundedSumCents int64
		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
		}
		// 更新订单状态为已退款
		if req.Amount == order.ActualAmount-refundedSumCents {
			_, 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
			}
			// 全额退款：恢复订单中使用的优惠券（优先使用结构化明细表）
			var cnt int64
			_ = h.repo.GetDbR().Raw("SELECT COUNT(*) FROM order_coupons WHERE order_id=?", order.ID).Scan(&cnt).Error
			if cnt > 0 {
				type ocRow struct {
					UserCouponID  int64
					AppliedAmount int64
				}
				var rows []ocRow
				_ = h.repo.GetDbR().Raw("SELECT user_coupon_id, applied_amount FROM order_coupons WHERE order_id=?", order.ID).Scan(&rows).Error
				for _, r := range rows {
					if r.UserCouponID > 0 && r.AppliedAmount > 0 {
						_ = h.repo.GetDbW().Exec("UPDATE user_coupons SET balance_amount=COALESCE(balance_amount,0)+?, status=1, used_order_id=0, used_at=NULL WHERE id=? AND user_id=?", r.AppliedAmount, r.UserCouponID, order.UserID).Error
					}
				}
			}
		}
		// 记录积分按比例恢复（幂等增量）
		if order.PointsAmount > 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 / 100
			toAdd := restorePointsTarget - restoredPointsSum
			maxPoints := order.PointsAmount / 100
			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
				}
			}
		}
		// 记录退款金额
		amountLedger := &model.UserPointsLedger{
			UserID:   order.UserID,
			Action:   "refund_amount",
			Points:   req.Amount / 100,
			RefTable: "payment_refund",
			RefID:    refundID,
			Remark:   req.Reason,
		}
		if err := h.writeDB.UserPointsLedger.WithContext(ctx.RequestContext()).Create(amountLedger); err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 160004, 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)
	}
}
