package data

import (
	"context"
	"fmt"
	"github.com/go-playground/validator/v10"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
	"github.com/wechatpay-apiv3/wechatpay-go/utils"
	"os"
	"sync"
	"voucher/internal/pkg/helper"
)

type Server struct {
	MchID                      string `validate:"required"`
	MchCertificateSerialNumber string `validate:"required"`
	WechatPayPublicKeyID       string `validate:"required"`
	Dir                        string
}

func (c *Server) Validate() error {

	if err := validator.New().Struct(c); err != nil {

		for _, err = range err.(validator.ValidationErrors) {
			return err
		}

	}

	return nil
}

func NewClient(ctx context.Context, c Server) (*core.Client, error) {

	dir, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	if c.Dir != "" {
		dir = c.Dir
	}

	filePath := fmt.Sprintf("%s/%s/%s/%s", dir, "cert", "wechat", c.MchID)

	if !helper.FileExists(filePath) {
		return nil, fmt.Errorf("MchID[%s]微信密钥证书信息不存在,请联系技术人员处理", c.MchID)
	}

	// 商户API私钥
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath(fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"))
	if err != nil {
		return nil, err
	}

	// 微信支付平台配置
	wechatPayPublicKey, err := utils.LoadPublicKeyWithPath(fmt.Sprintf("%s/%s", filePath, "pub_key.pem"))
	if err != nil {
		return nil, err
	}

	opts := []core.ClientOption{
		option.WithWechatPayPublicKeyAuthCipher(
			c.MchID,
			c.MchCertificateSerialNumber,
			mchPrivateKey,
			c.WechatPayPublicKeyID,
			wechatPayPublicKey,
		),
	}

	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		return nil, err
	}

	return client, nil
}

type manager struct {
	once    sync.Once
	mutex   sync.RWMutex
	clients map[string]*core.Client
}

var instance manager

func init() {
	instance = manager{
		clients: make(map[string]*core.Client),
	}
}

func GetClient(ctx context.Context, c Server) (*core.Client, error) {

	instance.mutex.Lock()
	defer instance.mutex.Unlock()

	if i, ok := instance.clients[c.MchID]; ok {
		return i, nil
	}

	var err error

	instance.once.Do(func() {

		i, err2 := NewClient(ctx, c)
		if err2 != nil {
			err = err2
			return
		}
		instance.clients[c.MchID] = i

	})

	if err != nil {
		return nil, err
	}

	return instance.clients[c.MchID], nil
}

func (s Server) Verify(ctx context.Context, message, signature string) error {

	verifier := verifiers.NewSHA256WithRSAVerifier(nil)

	if err := verifier.Verify(ctx, s.MchCertificateSerialNumber, message, signature); err != nil {
		return err
	}

	return nil
}
