package wechat

import (
	"context"
	"encoding/json"
	"os"
	"time"

	"bsm/full/module/finance/wallet/internal/config"
	"bsm/full/module/finance/wallet/internal/excode"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/wechat/v3"
)

type WeChatPay struct {
	ctx    context.Context
	Client *wechat.ClientV3
	Body   *gopay.BodyMap
}

var (
	mchID        string
	SerialNumber string
	mchAPIv3Key  string
	ApiClientKey string
	AppId        string
	Currency     string
	NotifyUrl    string
)

func new() {
	mchID = config.Spec.WeChat.MchID             // 商户号
	SerialNumber = config.Spec.WeChat.SerialNo   // 商户证书序列号
	mchAPIv3Key = config.Spec.WeChat.APIV3Key    // 商户APIv3密钥
	ApiClientKey = config.Spec.WeChat.PrivateKey // 私钥
	AppId = config.Spec.WeChat.AppID             // Appid
	Currency = config.Spec.WeChat.Currency       // 货币类型 CNY：人民币，境内商户号仅支持人民币。
	NotifyUrl = config.Spec.WeChat.NotifyURL     // 通知URL必须为直接可访问的URL，不允许携带查询串，要求必须为https地址。
}

func NewWechat() (*WeChatPay, error) {
	new()
	// NewClientV3 初始化微信客户端 v3
	//	mchid：商户ID 或者服务商模式的 sp_mchid
	// 	serialNo：商户证书的证书序列号
	//	apiV3Key：apiV3Key，商户平台获取
	//	privateKey：私钥 apiclient_key.pem 读取后的内容
	privateKey, err := os.ReadFile(ApiClientKey)
	if err != nil {
		return nil, err
	}
	client, err := wechat.NewClientV3(mchID, SerialNumber, mchAPIv3Key, string(privateKey))

	if err != nil {
		return nil, err
	}

	// 设置微信平台API证书和序列号（如开启自动验签，请忽略此步骤）
	//client.SetPlatformCert([]byte(""), "")

	// 启用自动同步返回验签，并定时更新微信平台API证书（开启自动验签时，无需单独设置微信平台API证书和序列号）
	err = client.AutoVerifySign()
	if err != nil {
		return nil, err
	}

	// 打开Debug开关，输出日志，默认是关闭的
	client.DebugSwitch = gopay.DebugOn

	return &WeChatPay{
		ctx:    context.Background(),
		Client: client,
	}, nil
}

// GetResponse tradeNo:finance_payment表的identity
func (srv *WeChatPay) GetResponse(Attach, OrderNo, desc, openid string, amount int64) (*wechat.Prepay, error) {
	expire := time.Now().Add(60 * time.Minute).Format(time.RFC3339)
	// 初始化 BodyMap
	bm := make(gopay.BodyMap)
	bm.Set("appid", AppId).
		Set("mchid", mchID).
		Set("description", desc).
		Set("attach", Attach). //finance_payment表的identity 附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用，实际情况下只有支付完成状态才会返回该字段
		Set("out_trade_no", OrderNo).
		Set("time_expire", expire).
		Set("notify_url", NotifyUrl).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", amount).
				Set("currency", Currency)
		}).
		SetBodyMap("payer", func(bm gopay.BodyMap) {
			bm.Set("openid", openid)
		})

	wxRsp, err := srv.Client.V3TransactionJsapi(srv.ctx, bm)
	if err != nil {
		return nil, err
	}

	return wxRsp.Response, nil
}

func (srv *WeChatPay) MiniPaySign(prepayid string) (map[string]string, error) {
	applet, err := srv.Client.PaySignOfApplet(AppId, prepayid)
	if err != nil {
		return nil, err
	}

	reply, err := struct2map(applet)
	if err != nil {
		return nil, err
	}

	return reply, nil
}

func (srv *WeChatPay) AppPaySign(prepayid string) (map[string]string, error) {
	app, err := srv.Client.PaySignOfApp(AppId, prepayid)
	if err != nil {
		return nil, err
	}

	reply, err := struct2map(app)
	if err != nil {
		return nil, err
	}

	return reply, nil
}

func (srv *WeChatPay) JsapiPaySign(prepayid string) (map[string]string, error) {
	jsapi, err := srv.Client.PaySignOfJSAPI(AppId, prepayid)
	if err != nil {
		return nil, err
	}

	reply, err := struct2map(jsapi)
	if err != nil {
		return nil, err
	}

	return reply, nil
}

func (srv *WeChatPay) NativePaySign(Attach, OrderNo, description, expire, openid string, amount int64) (map[string]string, error) {
	// 初始化 BodyMap
	body := make(gopay.BodyMap)
	body.Set("appid", AppId).
		Set("mchid", mchID).
		Set("description", description).
		Set("attach", Attach). //finance_payment表的identity 附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用，实际情况下只有支付完成状态才会返回该字段
		Set("out_trade_no", OrderNo).
		Set("time_expire", expire).
		Set("notify_url", NotifyUrl).
		SetBodyMap("amount", func(bm gopay.BodyMap) {
			bm.Set("total", amount).
				Set("currency", Currency)
		}).
		SetBodyMap("payer", func(bm gopay.BodyMap) {
			bm.Set("openid", openid)
		})

	native, err := srv.Client.V3TransactionNative(srv.ctx, body)
	if err != nil {
		return nil, err
	}
	reply, err := struct2map(native)
	if err != nil {
		return nil, err
	}
	return reply, nil
}

func struct2map(in any) (map[string]string, error) {
	b, err := json.Marshal(in)
	var m map[string]string
	err = json.Unmarshal(b, &m)
	return m, err
}

func CheckParam(amount int64, auth_code string, identity string, order_no string) (err error) {
	if amount == 0 {
		return excode.ErrAmount
	}
	if auth_code == "" {
		return excode.ErrAuthCode
	}
	if identity == "" {
		return excode.ErrPassportId
	}
	if order_no == "" {
		return excode.ErrOrderNo
	}
	return nil
}
