package controller

import (
	"context"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"math"
	"net/http"
	"os"
	"strconv"
	"strings"
	"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/wechat/v3"
	"github.com/thanhpk/randstr"
)

const (
	PaymentMethodWechatPay = "wechat_pay"
)

var wechatPayClientMu sync.Mutex
var wechatPayClient *wechat.ClientV3

// ResetWechatPayClient 重置微信支付客户端（配置变更时调用）
func ResetWechatPayClient() {
	wechatPayClientMu.Lock()
	wechatPayClient = nil
	wechatPayClientMu.Unlock()
}

func init() {
	setting.OnWechatPayConfigChanged = ResetWechatPayClient
}

// getWechatPayClient 获取或创建微信支付 V3 客户端
func getWechatPayClient() (*wechat.ClientV3, error) {
	wechatPayClientMu.Lock()
	defer wechatPayClientMu.Unlock()

	if wechatPayClient != nil {
		return wechatPayClient, nil
	}

	if !setting.IsWechatPayConfigured() {
		return nil, fmt.Errorf("微信支付未配置")
	}

	var privateKey string
	if setting.WechatPayKeyB64 != "" {
		keyBytes, err := base64.StdEncoding.DecodeString(setting.WechatPayKeyB64)
		if err != nil {
			return nil, fmt.Errorf("解析商户私钥 Base64 失败: %w", err)
		}
		privateKey = wrapAsPEM(keyBytes, "PRIVATE KEY")
	} else {
		keyBytes, err := os.ReadFile(setting.WechatPayKeyPath)
		if err != nil {
			return nil, fmt.Errorf("读取商户私钥文件失败: %w", err)
		}
		privateKey = string(keyBytes)
	}

	client, err := wechat.NewClientV3(setting.WechatPayMchID, setting.WechatPaySerialNo, setting.WechatPayAPIv3Key, privateKey)
	if err != nil {
		return nil, fmt.Errorf("创建微信支付客户端失败: %w", err)
	}

	// 设置微信支付公钥（用于回调验签）
	if setting.WechatPayPubKeyB64 != "" {
		pubKeyBytes, err := base64.StdEncoding.DecodeString(setting.WechatPayPubKeyB64)
		if err != nil {
			return nil, fmt.Errorf("解析微信公钥 Base64 失败: %w", err)
		}
		if err := client.AutoVerifySignByPublicKey([]byte(wrapAsPEM(pubKeyBytes, "PUBLIC KEY")), setting.WechatPayPubKeyID); err != nil {
			return nil, fmt.Errorf("开启公钥验签失败: %w", err)
		}
	} else {
		pubKeyBytes, err := os.ReadFile(setting.WechatPayPubKeyPath)
		if err != nil {
			return nil, fmt.Errorf("读取微信公钥文件失败: %w", err)
		}
		if err := client.AutoVerifySignByPublicKey([]byte(wrapAsPEM(pubKeyBytes, "PUBLIC KEY")), setting.WechatPayPubKeyID); err != nil {
			return nil, fmt.Errorf("开启公钥验签失败: %w", err)
		}
	}

	wechatPayClient = client
	return wechatPayClient, nil
}

// wrapAsPEM 确保 keyBytes 是 PEM 格式，兼容多种输入：
// 1. 已是 PEM（含 -----BEGIN）→ 原样返回
// 2. 是 DER base64 文本（如 PEM 内层内容）→ 解码为 DER 后包装为 PEM
// 3. 是原始 DER 字节 → 直接包装为 PEM
func wrapAsPEM(keyBytes []byte, keyType string) string {
	content := string(keyBytes)
	if strings.Contains(content, "-----BEGIN") {
		return content
	}
	// 尝试 base64 解码：内容可能是 PEM 文件内层的 base64 文本
	if derBytes, err := base64.StdEncoding.DecodeString(content); err == nil && len(derBytes) > 0 {
		return string(pem.EncodeToMemory(&pem.Block{
			Type:  keyType,
			Bytes: derBytes,
		}))
	}
	// 当作原始 DER 字节
	return string(pem.EncodeToMemory(&pem.Block{
		Type:  keyType,
		Bytes: keyBytes,
	}))
}

// WechatPayRequest 微信支付请求参数
type WechatPayRequest struct {
	Amount int64 `json:"amount"`
}

// createWechatNativeOrder 调用微信 Native 下单 API，返回二维码 URL
func createWechatNativeOrder(client *wechat.ClientV3, description, tradeNo string, totalFee int) (string, error) {
	bm := make(gopay.BodyMap)
	bm.Set("appid", setting.WechatPayAppID).
		Set("mchid", setting.WechatPayMchID).
		Set("description", description).
		Set("out_trade_no", tradeNo).
		Set("notify_url", setting.WechatPayNotifyURL).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", totalFee).
				Set("currency", "CNY")
		})

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

	rx, err := client.V3TransactionNative(ctx, bm)
	if err != nil {
		return "", fmt.Errorf("微信统一下单失败: %w", err)
	}

	if rx.Code != wechat.Success {
		return "", fmt.Errorf("微信支付错误: %d - %s", rx.Code, rx.ErrResponse.Message)
	}

	return rx.Response.CodeUrl, nil
}

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

	minTopup := getWechatMinTopup()
	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 := getWechatPayMoney(float64(req.Amount), group)
	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)})
}

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

	minTopup := getWechatMinTopup()
	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.IsWechatPayConfigured() {
		c.JSON(200, gin.H{"message": "error", "data": "微信支付未配置"})
		return
	}

	client, err := getWechatPayClient()
	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("wx%d%s", time.Now().UnixMilli(), randstr.String(8))

	payMoney := getWechatPayMoney(float64(req.Amount), group)
	totalFee := int(math.Round(payMoney * 100)) // 元 -> 分

	codeUrl, err := createWechatNativeOrder(client, fmt.Sprintf("充值%d", req.Amount), tradeNo, totalFee)
	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: PaymentMethodWechatPay,
		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": codeUrl,
		},
	})
}

// WechatPayStatus 轮询微信支付订单状态
func WechatPayStatus(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,
		},
	})
}

// WechatPayWebhook 处理微信支付回调通知
func WechatPayWebhook(c *gin.Context) {
	notifyReq, err := wechat.V3ParseNotify(c.Request)
	if err != nil {
		log.Printf("解析微信支付回调失败: %v", err)
		c.JSON(http.StatusBadRequest, gin.H{"code": "FAIL", "message": "解析回调失败"})
		return
	}

	client, err := getWechatPayClient()
	if err != nil {
		log.Printf("获取微信支付客户端失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"code": "FAIL", "message": "服务错误"})
		return
	}

	if err := notifyReq.VerifySignByPK(client.WxPublicKey()); err != nil {
		log.Printf("微信支付回调验签失败: %v", err)
		c.JSON(http.StatusBadRequest, gin.H{"code": "FAIL", "message": "验签失败"})
		return
	}

	result, err := notifyReq.DecryptPayCipherText(setting.WechatPayAPIv3Key)
	if err != nil {
		log.Printf("解密微信支付回调数据失败: %v", err)
		c.JSON(http.StatusBadRequest, gin.H{"code": "FAIL", "message": "解密失败"})
		return
	}

	if result.TradeState != "SUCCESS" {
		log.Printf("微信支付回调非成功状态: %s, 订单号: %s", result.TradeState, result.OutTradeNo)
		c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "成功"})
		return
	}

	tradeNo := result.OutTradeNo

	LockOrder(tradeNo)
	defer UnlockOrder(tradeNo)

	if err := model.CompleteSubscriptionOrder(tradeNo, common.GetJsonString(result)); err == nil {
		log.Printf("微信支付订阅订单完成: %s", tradeNo)
		c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "成功"})
		return
	} else if !errors.Is(err, model.ErrSubscriptionOrderNotFound) {
		log.Printf("微信支付订阅订单处理失败: %s, err: %s", tradeNo, err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"code": "FAIL", "message": "处理失败"})
		return
	}

	if err := model.RechargeWechat(tradeNo); err != nil {
		log.Printf("微信支付充值失败: %s, err: %s", tradeNo, err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"code": "FAIL", "message": "处理失败"})
		return
	}

	log.Printf("微信支付充值成功: %s, 金额: %d", tradeNo, result.Amount.Total)
	c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "message": "成功"})
}

func getWechatPayMoney(amount float64, group string) float64 {
	return calcPayMoney(amount, group, setting.WechatPayUnitPrice)
}

func getWechatMinTopup() int64 {
	return calcMinTopup(setting.WechatPayMinTopUp)
}
