package controller

import (
	"context"
	"encoding/base64"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/QuantumNous/new-api/common"
	"github.com/QuantumNous/new-api/model"
	"github.com/QuantumNous/new-api/setting"

	"github.com/gin-gonic/gin"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/alipay"
	"github.com/thanhpk/randstr"
)

const (
	PaymentMethodAlipay = "alipay"
)

var alipayClientMu sync.Mutex
var alipayClient *alipay.Client

// ResetAlipayClient 重置支付宝客户端（配置变更时调用）
func ResetAlipayClient() {
	alipayClientMu.Lock()
	alipayClient = nil
	alipayClientMu.Unlock()
}

func init() {
	setting.OnAlipayConfigChanged = ResetAlipayClient
}

// getAlipayClient 获取或创建支付宝客户端
func getAlipayClient() (*alipay.Client, error) {
	alipayClientMu.Lock()
	defer alipayClientMu.Unlock()

	if alipayClient != nil {
		return alipayClient, nil
	}

	if !setting.IsAlipayConfigured() {
		return nil, fmt.Errorf("支付宝未配置")
	}

	client, err := alipay.NewClient(setting.AlipayAppID, setting.AlipayPrivateKey, true)
	if err != nil {
		return nil, fmt.Errorf("创建支付宝客户端失败: %w", err)
	}

	client.SetCharset("utf-8").
		SetSignType(alipay.RSA2).
		SetNotifyUrl(setting.AlipayNotifyURL)

	// 设置支付宝公钥（用于回调验签）
	pubKeyBytes, err := base64.StdEncoding.DecodeString(setting.AlipayPublicKey)
	if err != nil {
		pubKeyBytes = []byte(setting.AlipayPublicKey)
	}
	client.AutoVerifySign([]byte(wrapAsPEM(pubKeyBytes, "PUBLIC KEY")))

	alipayClient = client
	return alipayClient, nil
}

// AlipayPayRequest 支付宝支付请求参数
type AlipayPayRequest struct {
	Amount int64 `json:"amount"`
}

// createAlipayPrecreateOrder 调用支付宝当面付预下单 API，返回二维码内容
func createAlipayPrecreateOrder(client *alipay.Client, subject, tradeNo string, totalAmount string) (string, error) {
	bm := make(gopay.BodyMap)
	bm.Set("subject", subject).
		Set("out_trade_no", tradeNo).
		Set("total_amount", totalAmount).
		Set("product_code", "FACE_TO_FACE_PAYMENT")

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	rsp, err := client.TradePrecreate(ctx, bm)
	if err != nil {
		return "", fmt.Errorf("支付宝当面付下单失败: %w", err)
	}

	if rsp.Response.Code != "10000" {
		return "", fmt.Errorf("支付宝错误: %s - %s", rsp.Response.Code, rsp.Response.Msg)
	}

	return rsp.Response.QrCode, nil
}

// RequestAlipayPayAmount 计算支付宝应付金额
func RequestAlipayPayAmount(c *gin.Context) {
	var req AlipayPayRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
		return
	}

	minTopup := calcMinTopup(setting.AlipayMinTopUp)
	if req.Amount < minTopup {
		c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", minTopup)})
		return
	}

	id := c.GetInt("id")
	group, err := model.GetUserGroup(id, true)
	if err != nil {
		c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
		return
	}

	payMoney := calcPayMoney(float64(req.Amount), group, setting.AlipayUnitPrice)
	if payMoney <= 0.01 {
		c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"})
		return
	}

	c.JSON(200, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)})
}

// RequestAlipayPay 创建支付宝支付订单，返回二维码 URL
func RequestAlipayPay(c *gin.Context) {
	var req AlipayPayRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
		return
	}

	minTopup := calcMinTopup(setting.AlipayMinTopUp)
	if req.Amount < minTopup {
		c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", minTopup)})
		return
	}
	if req.Amount > 10000 {
		c.JSON(200, gin.H{"message": "error", "data": "充值数量不能大于 10000"})
		return
	}

	if !setting.IsAlipayConfigured() {
		c.JSON(200, gin.H{"message": "error", "data": "支付宝未配置"})
		return
	}

	client, err := getAlipayClient()
	if err != nil {
		log.Println("获取支付宝客户端失败:", err)
		c.JSON(200, gin.H{"message": "error", "data": "支付宝配置错误"})
		return
	}

	id := c.GetInt("id")

	group, err := model.GetUserGroup(id, true)
	if err != nil {
		c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"})
		return
	}

	topupGroupRatio := common.GetTopupGroupRatio(group)
	if topupGroupRatio == 0 {
		topupGroupRatio = 1
	}
	chargedMoney := float64(req.Amount) * topupGroupRatio

	tradeNo := fmt.Sprintf("ali%d%s", time.Now().UnixMilli(), randstr.String(8))

	payMoney := calcPayMoney(float64(req.Amount), group, setting.AlipayUnitPrice)
	totalAmount := strconv.FormatFloat(payMoney, 'f', 2, 64)

	qrCode, err := createAlipayPrecreateOrder(client, fmt.Sprintf("充值%d", req.Amount), tradeNo, totalAmount)
	if err != nil {
		log.Println(err)
		c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"})
		return
	}

	topUp := &model.TopUp{
		UserId:        id,
		Amount:        req.Amount,
		Money:         chargedMoney,
		TradeNo:       tradeNo,
		PaymentMethod: PaymentMethodAlipay,
		CreateTime:    time.Now().Unix(),
		Status:        common.TopUpStatusPending,
	}
	if err := topUp.Insert(); err != nil {
		c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"})
		return
	}

	c.JSON(200, gin.H{
		"message": "success",
		"data": gin.H{
			"trade_no":    tradeNo,
			"qr_code_url": qrCode,
		},
	})
}

// AlipayPayStatus 轮询支付宝支付订单状态
func AlipayPayStatus(c *gin.Context) {
	tradeNo := c.Query("trade_no")
	if tradeNo == "" {
		c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
		return
	}

	topUp := model.GetTopUpByTradeNo(tradeNo)
	if topUp == nil {
		c.JSON(200, gin.H{"message": "error", "data": "订单不存在"})
		return
	}

	userId := c.GetInt("id")
	if topUp.UserId != userId {
		c.JSON(200, gin.H{"message": "error", "data": "订单不存在"})
		return
	}

	c.JSON(200, gin.H{
		"message": "success",
		"data": gin.H{
			"status": topUp.Status,
			"amount": topUp.Amount,
		},
	})
}

// AlipayPayWebhook 处理支付宝异步回调通知
func AlipayPayWebhook(c *gin.Context) {
	notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request)
	if err != nil {
		log.Printf("解析支付宝回调失败: %v", err)
		c.String(http.StatusBadRequest, "fail")
		return
	}

	ok, err := alipay.VerifySign(setting.AlipayPublicKey, notifyReq)
	if err != nil {
		log.Printf("支付宝回调验签失败: %v", err)
		c.String(http.StatusBadRequest, "fail")
		return
	}
	if !ok {
		log.Printf("支付宝回调验签不通过")
		c.String(http.StatusBadRequest, "fail")
		return
	}

	tradeStatus := notifyReq.Get("trade_status")
	if tradeStatus != "TRADE_SUCCESS" {
		c.String(http.StatusOK, "success")
		return
	}

	tradeNo := notifyReq.Get("out_trade_no")

	LockOrder(tradeNo)
	defer UnlockOrder(tradeNo)

	if err := model.RechargeAlipay(tradeNo); err != nil {
		log.Printf("支付宝充值失败: %s, err: %s", tradeNo, err.Error())
		c.String(http.StatusInternalServerError, "fail")
		return
	}

	log.Printf("支付宝充值成功: %s", tradeNo)
	c.String(http.StatusOK, "success")
}
