package app

import (
	"net/http"

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

type jsapiPreorderRequest struct {
	OrderNo string `json:"order_no" form:"order_no"`
	OpenID  string `json:"openid" form:"openid"`
}
type jsapiPreorderResponse struct {
	TimeStamp string `json:"timeStamp"`
	NonceStr  string `json:"nonceStr"`
	Package   string `json:"package"`
	SignType  string `json:"signType"`
	PaySign   string `json:"paySign"`
}

// WechatJSAPIPreorder 小程序微信支付预下单并返回调起参数
// 入参：order_no(业务订单号)、openid(用户在小程序的openid)
// 返回：用于wx.requestPayment的参数(timeStamp/nonceStr/package/signType/paySign)
// 错误：参数绑定失败、配置缺失、订单不存在或状态不合法
// @Summary 小程序微信支付预下单
// @Description 根据`order_no`与`openid`创建或复用JSAPI预下单，返回`wx.requestPayment`调起参数；订单需为当前登录用户且状态为待支付
// @Tags APP端.支付
// @Accept json
// @Produce json
// @Security LoginVerifyToken
// @Param RequestBody body jsapiPreorderRequest true "请求参数：业务订单号与openid"
// @Success 200 {object} jsapiPreorderResponse
// @Failure 400 {object} code.Failure "参数错误/配置缺失/订单不存在或状态不合法/微信预下单失败/签名构建失败"
// @Router /api/app/pay/wechat/jsapi/preorder [post]
func (h *handler) WechatJSAPIPreorder() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(jsapiPreorderRequest)
		rsp := new(jsapiPreorderResponse)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}
		if ok, err := pay.ValidateConfig(ctx.RequestContext()); !ok {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 140001, err.Error()))
			return
		}
		// Use dynamic configurations
		dynamicDC := sysconfig.GetDynamicConfig()
		wxCfg := dynamicDC.GetWechat(ctx.RequestContext().Context)
		wxPayCfg := dynamicDC.GetWechatPay(ctx.RequestContext().Context)

		if req.OrderNo == "" || req.OpenID == "" {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 140002, "order_no/openid required"))
			return
		}
		// 查询订单并校验状态=1 待支付，归属当前用户
		order, err := h.readDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.OrderNo.Eq(req.OrderNo), h.readDB.Orders.UserID.Eq(int64(ctx.SessionUserInfo().Id)), h.readDB.Orders.Status.Eq(1)).First()
		if err != nil || order == nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 140006, "order not found or status invalid"))
			return
		}
		// 幂等：若已存在同一 out_trade_no 的预下单记录，直接复用其 prepay_id
		existed, _ := h.readDB.PaymentPreorders.WithContext(ctx.RequestContext()).Where(h.readDB.PaymentPreorders.OutTradeNo.Eq(order.OrderNo)).First()
		var prepayID string
		if existed != nil {
			prepayID = existed.PrepayID
			if order.PayPreorderID == 0 {
				_, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{
					h.readDB.Orders.PayPreorderID.ColumnName().String(): existed.ID,
				})
			}
		} else {
			wc, err := pay.NewWechatPayClient(ctx.RequestContext())
			if err != nil {
				ctx.AbortWithError(core.Error(http.StatusBadRequest, 140004, err.Error()))
				return
			}
			pid, err := wc.JSAPIPrepay(ctx.RequestContext(), wxCfg.AppID, wxPayCfg.MchID, "订单"+req.OrderNo, req.OrderNo, order.ActualAmount, req.OpenID, wxPayCfg.NotifyURL)
			if err != nil {
				ctx.AbortWithError(core.Error(http.StatusBadRequest, 140005, err.Error()))
				return
			}
			prepayID = pid
			pre := &model.PaymentPreorders{OrderID: order.ID, OrderNo: order.OrderNo, OutTradeNo: order.OrderNo, PrepayID: prepayID, AmountTotal: order.ActualAmount, PayerOpenid: req.OpenID, NotifyURL: wxPayCfg.NotifyURL, Status: "created"}
			if err := h.writeDB.PaymentPreorders.WithContext(ctx.RequestContext()).Omit(h.writeDB.PaymentPreorders.ExpiredAt).Create(pre); err == nil {
				_, _ = h.writeDB.Orders.WithContext(ctx.RequestContext()).Where(h.readDB.Orders.ID.Eq(order.ID)).Updates(map[string]any{h.readDB.Orders.PayPreorderID.ColumnName().String(): pre.ID})
			}
		}
		ts, nonce, pkg, signType, paySign, err := pay.BuildJSAPIParams(ctx.RequestContext(), wxCfg.AppID, prepayID)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 140003, err.Error()))
			return
		}
		rsp.TimeStamp = ts
		rsp.NonceStr = nonce
		rsp.Package = pkg
		rsp.SignType = signType
		rsp.PaySign = paySign
		ctx.Payload(rsp)
	}
}
