package service

import (
	"context"
	"f5"
	"fmt"
	"main/mt"
	"os"
	"q5"
	"time"

	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/consts"
	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
	"github.com/wechatpay-apiv3/wechatpay-go/services/certificates"
	"github.com/wechatpay-apiv3/wechatpay-go/services/fileuploader"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	"github.com/wechatpay-apiv3/wechatpay-go/utils"
)

func (wp *wxpay) initMch() {
	var (
		mchID                      string = mt.Table.Config.GetWxMerchantId()    // 商户号
		mchCertificateSerialNumber string = mt.Table.Config.GetWxCertificateSn() // 商户证书序列号
		// mchAPIv3Key                string = mt.Table.Config.GetWxMerchantApiKey() // 商户APIv3密钥
		mchPubKeyId string = mt.Table.Config.GetWxMerchantPublicKeyId()
	)

	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath("../res/kingsome_key.pem")
	if err != nil {
		f5.GetSysLog().Alert("load merchant private key error")
	}
	mchPublicKey, err := utils.LoadPublicKeyWithPath("../res/kingsome_pub_key.pem")
	if err != nil {
		f5.GetSysLog().Alert("load merchant public key error")
	}

	wp.ctx = context.Background()
	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		// option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
		option.WithWechatPayPublicKeyAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchPubKeyId, mchPublicKey),
	}
	wp.client, err = core.NewClient(wp.ctx, opts...)
	if err != nil {
		f5.GetSysLog().Alert("new wechat pay client err:%s", err)
	}

	// 发送请求，以下载微信支付平台证书为例
	// https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay5_1.shtml
	svc := certificates.CertificatesApiService{Client: wp.client}
	resp, result, err := svc.DownloadCertificates(wp.ctx)
	f5.GetSysLog().Debug("status=%d resp=%s err=%s", result.Response.StatusCode, resp, err.Error())

	go wp.checkGameMediaId()

	wp.payhtmlstr, err = f5.ReadJsonFile("../config/payhtml.template")

	if err != nil {
		f5.GetSysLog().Error("%s", err.Error())
	}
}

func (wp *wxpay) checkGameMediaId() {
	wp.mediaInfo = q5.ConcurrentMap[int64, string]{}
	uploadtime := int64(0)
	for {
		nowtime := f5.GetApp().GetRealSeconds()
		if wp.mediaInfo.GetSize() == 0 || q5.GetDaySeconds(uploadtime, 8) != q5.GetDaySeconds(nowtime, 8) {
			uploader := fileuploader.ImageUploader{Client: wp.client}
			mt.Table.Wxconfig.Traverse(func(w *mt.Wxconfig) bool {
				filename := q5.SafeToString(w.GetGameid()) + ".jpg"
				file, err := os.Open("../res/gamepics/" + filename)
				if err != nil {
					f5.GetSysLog().Error("file error:%s", filename)
					return false
				}
				defer file.Close()

				resp, result, err := uploader.Upload(wp.ctx, file, filename, consts.ImageJPG)
				if err == nil {
					wp.mediaInfo.Store(w.GetGameid(), *resp.MediaId)
					f5.GetSysLog().Debug("refresh media ok:%s, %s", filename, *resp.MediaId)
				} else {
					f5.GetSysLog().Error("refresh media err:%s, %s", filename, result.Response.Status)
				}

				return true
			})

			uploadtime = nowtime
		}
		time.Sleep(time.Minute)
	}
}

func (wp *wxpay) GenThumburl(gameid int64) (thumburl string) {
	token := wp.getAccessToken(gameid)
	mediaid := ""
	pmediaid, ok := wp.mediaInfo.Load(gameid)
	if ok {
		mediaid = *pmediaid
	}

	urls := mt.Table.Config.GetWxUrl()
	if len(urls) > 0 {
		thumburl = fmt.Sprintf("https://%s/cgi-bin/media/get?access_token=%s&type=image&media_id=%s", urls[0], token, mediaid)
	}

	return
}

func (wp *wxpay) GenSendWxMsgUrl(gameid int64) (msgurl string) {
	token := wp.getAccessToken(gameid)
	urls := mt.Table.Config.GetWxUrl()
	if len(urls) > 0 {
		msgurl = fmt.Sprintf("https://%s/cgi-bin/message/custom/send?access_token=%s", urls[0], token)
	}

	return
}

func (wp *wxpay) GetPrepayInfoStr(openid string, gameid int64, userip string, orderid string, goodsid int64) (rspstr string) {
	svc := jsapi.JsapiApiService{Client: wp.client}
	// 得到prepay_id，以及调起支付所需的参数和签名
	cfg := mt.Table.Wxconfig.GetById(gameid)
	goods, ok := wp.gamesGoods.Load(gameid)
	if !ok {
		return
	}

	info, ok := (*goods)[goodsid]
	if !ok {
		return
	}

	resp, result, err := svc.PrepayWithRequestPayment(wp.ctx,
		jsapi.PrepayRequest{
			Appid:       core.String(cfg.GetAppid()),
			Mchid:       core.String(mt.Table.Config.GetWxMerchantId()),
			Description: core.String(info.Name),
			OutTradeNo:  core.String(orderid),
			Attach:      core.String(userip),
			NotifyUrl:   core.String(mt.Table.Config.GetWxNofityHost() + "/wx/paynotify"),
			Amount: &jsapi.Amount{
				Total: core.Int64(info.Count),
			},
			Payer: &jsapi.Payer{
				Openid: core.String(openid),
			},
		},
	)

	f5.GetSysLog().Debug("prepay rsp:%s, %s", fmt.Sprintln(resp.String()), result.Response.Status)
	if err != nil {
		return
	}

	rspstr = fmt.Sprintf(wp.payhtmlstr, resp.Appid, resp.TimeStamp, resp.NonceStr, resp.Package, resp.PaySign)
	return
}

func (wp *wxpay) VerifyPaySign(rawdata string, signature string) bool {
	sign, err := wp.client.Sign(wp.ctx, rawdata)
	f5.GetSysLog().Debug("verify pay sign:%s, signature:%s", sign.Signature, signature)
	if err != nil || sign.Signature != signature {
		return false
	}

	return true
}

func (wp *wxpay) DecryptPaydata(associatedData string, nonce string, clipherdata string) (plaindata string, err error) {
	return utils.DecryptAES256GCM(mt.Table.Config.GetWxMerchantApiKey(), associatedData, nonce, clipherdata)
}
