package pay

import (
	"crypto"
	crand "crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	mrand "math/rand"
	"os"
	"sync"
	"time"

	"bindbox-game/configs"
)

// 私钥缓存 - 避免每次请求都从磁盘读取
var (
	cachedRSAKey     *rsa.PrivateKey
	rsaKeyOnce       sync.Once
	rsaKeyLoadErr    error
	rsaKeyConfigPath string // 记录加载时的路径，用于检测配置变更
)

// loadRSAPrivateKey 从磁盘加载私钥（内部函数，仅在首次调用时执行）
func loadRSAPrivateKey(keyPath string) (*rsa.PrivateKey, error) {
	b, err := os.ReadFile(keyPath)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(b)
	if block == nil {
		return nil, errors.New("invalid merchant private key pem")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		// 兼容PKCS1
		k1, e1 := x509.ParsePKCS1PrivateKey(block.Bytes)
		if e1 != nil {
			return nil, err
		}
		key = k1
	}
	rsaKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return nil, errors.New("merchant private key is not RSA")
	}
	return rsaKey, nil
}

// getCachedRSAKey 获取缓存的RSA私钥
func getCachedRSAKey(keyPath string) (*rsa.PrivateKey, error) {
	rsaKeyOnce.Do(func() {
		rsaKeyConfigPath = keyPath
		cachedRSAKey, rsaKeyLoadErr = loadRSAPrivateKey(keyPath)
	})
	// 如果配置路径变更（理论上不应该发生），返回错误以提示重启
	if rsaKeyConfigPath != keyPath {
		return nil, errors.New("private key path changed, please restart the server")
	}
	return cachedRSAKey, rsaKeyLoadErr
}

// BuildJSAPIParams 为小程序支付构造客户端参数
// 入参：appid(微信小程序AppID)、prepayID(统一下单返回的prepay_id)
// 返回：timeStamp、nonceStr、package(格式为"prepay_id=***" )、signType(固定"RSA")、paySign(RSA-SHA256签名)
// 错误：当私钥读取或签名失败时返回错误
func BuildJSAPIParams(appid string, prepayID string) (timeStamp string, nonceStr string, pkg string, signType string, paySign string, err error) {
	cfg := configs.Get()
	if cfg.WechatPay.PrivateKeyPath == "" {
		return "", "", "", "", "", errors.New("wechat pay private key path not configured")
	}

	// 使用缓存的私钥，避免每次都从磁盘读取
	rsaKey, err := getCachedRSAKey(cfg.WechatPay.PrivateKeyPath)
	if err != nil {
		return "", "", "", "", "", err
	}

	ts := fmt.Sprintf("%d", time.Now().Unix())
	// 随机串32位
	const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	rb := make([]byte, 32)
	for i := range rb {
		rb[i] = letters[mrand.Intn(len(letters))]
	}
	nonce := string(rb)
	pkg = "prepay_id=" + prepayID
	signType = "RSA"

	message := fmt.Sprintf("%s\n%s\n%s\n%s\n", appid, ts, nonce, pkg)
	h := sha256.Sum256([]byte(message))
	sig, err := rsa.SignPKCS1v15(crand.Reader, rsaKey, crypto.SHA256, h[:])
	if err != nil {
		return "", "", "", "", "", err
	}
	paySign = base64.StdEncoding.EncodeToString(sig)
	return ts, nonce, pkg, signType, paySign, nil
}

// ValidateConfig 校验微信支付必要配置
// 入参：无
// 返回：true表示配置齐全；false表示缺失，并附带错误信息
func ValidateConfig() (bool, error) {
	c := configs.Get()
	if c.Wechat.AppID == "" {
		return false, errors.New("wechat app_id missing")
	}
	if c.WechatPay.MchID == "" || c.WechatPay.SerialNo == "" || c.WechatPay.PrivateKeyPath == "" || c.WechatPay.ApiV3Key == "" {
		return false, errors.New("wechat pay config incomplete")
	}
	return true, nil
}
