package talent_service

import (
	"context"
	"fmt"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/os/glog"
	"github.com/gogf/gf/os/gtime"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	wxUtils "github.com/wechatpay-apiv3/wechatpay-go/utils"
	"math/rand"
	"strconv"
	"time"
	"youngmini_server/app/dao"
	"youngmini_server/app/model"
	"youngmini_server/app/model/talent_model"
	"youngmini_server/app/utils"
)

var (
	mchID                      string = "1615933939"                                // 商户号
	mchCertificateSerialNumber string = "636DC3B258E436FAD123DAC6453E485CCDBF8770"  // 商户证书序列号
	mchAPIv3Key                string = "zwbx8iae5llhpwj48guterlpodt3xngo"          // 商户APIv3密钥
	keyFilePath                string = "./config/secret/apiclient_key.pem"         // 密钥文件路径
)

func generatePayOrderNumber(talentId int) string {
	orderNo := gtime.Now().Format("YmdHis")
	rand.Seed(time.Now().UnixNano())
	r := rand.Intn(899)
	orderNo += strconv.Itoa(r + 100)
	orderNo += fmt.Sprintf("%04d", talentId)

	return orderNo
}

func WxPayOnTalentRequestPay(r *ghttp.Request) *TalentHttpResult {
	tid, err := utils.SessionTalentInfo.GetTalentIdFromSession(r)
	if err != nil {
		return &TalentHttpResult{Code: -1, Msg: "Get talent id failed"}
	}

	// 获取上传参数
	var payInfo *talent_model.WxPrepayRequestData
	err = r.ParseForm(&payInfo)
	if err != nil {
		return &TalentHttpResult{Code: -2, Msg: "param error"}
	}

	// 获取达人信息
	var talentInfo *model.TalentInfo
	err = g.DB().Model(dao.TalentInfo.Table).Scan(&talentInfo, dao.TalentInfo.Columns.Id, tid)
	if err != nil {
		return &TalentHttpResult{Code: -3, Msg: "query talent info failed"}
	}

	if talentInfo.InBlacklist >= 1 {
		return &TalentHttpResult{Code: -4, Msg: "talent in blacklist"}
	}

	// 获取任务信息
	var taskInfo *talent_model.TaskDetail
	err = g.DB().Model(dao.TaskBaseInfo.Table).WithAll().Where(dao.TaskBaseInfo.Columns.TaskId, payInfo.TaskId).Scan(&taskInfo)
	if err != nil || taskInfo == nil {
		return &TalentHttpResult{Code: -5, Msg: "query task info failed"}
	}

	// 任务当前不是执行状态，返回
	if taskInfo.TaskStatus != 2 {
		return &TalentHttpResult{Code: -6, Msg: "task can not sign up"}
	}

	// 任务已截止报名
	if taskInfo.DeadlineTime.Before(gtime.Now()) {
		return &TalentHttpResult{Code: -7, Msg: "task sign up has finished"}
	}

	// 如果任务不需要拍单，或拍单方式为不拍单则返回
	if taskInfo.IsBuySamples == nil || taskInfo.IsBuySamples.BuySamplesType != 1 {
		return &TalentHttpResult{Code: -8, Msg: "task do not need buy samples"}
	}

	// 任务拍单费用和上传的拍单费用不符，返回
	if taskInfo.IsBuySamples.BuySamplesCost != payInfo.BuySamplesCost {
		return &TalentHttpResult{Code: -9, Msg: "task buy samples cost != input cost"}
	}

	mchPrivateKey, err := wxUtils.LoadPrivateKeyWithPath(keyFilePath)
	if err != nil {
		glog.Error("load merchant private key error")
		return &TalentHttpResult{Code: -10, Msg: "load client key failed"}
	}

	ctx := context.Background()
	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		glog.Error("new wechat pay client err:", err)
		return &TalentHttpResult{Code: -11, Msg: "create client failed"}
	}

	orderNumber := generatePayOrderNumber(talentInfo.Id)
	svc := jsapi.JsapiApiService{Client: client}
	// 得到prepay_id，以及调起支付所需的参数和签名
	resp, result, err := svc.PrepayWithRequestPayment(ctx,
		jsapi.PrepayRequest{
			Appid:       core.String(g.Config().GetString("miniapp.appid")),
			Mchid:       core.String(mchID),
			Description: core.String("拍单费用_" + strconv.Itoa(taskInfo.TaskId) + "_" +taskInfo.TaskName + "_" + payInfo.PlatformNickname),
			OutTradeNo:  core.String(orderNumber),
			Attach:      core.String("报名任务拍单费用"),
			NotifyUrl:   core.String("https://www.weixin.qq.com/wxpay/pay.php"),
			Amount: &jsapi.Amount{
				Total: core.Int64(payInfo.BuySamplesCost),
			},
			Payer: &jsapi.Payer{
				Openid: core.String(talentInfo.TalentWxOpenid),
			},
		},
	)

	if err != nil {
		// 处理错误
		glog.Error("call Prepay err:%s", err)
		return &TalentHttpResult{Code: -12, Msg: "request prepay info failed"}
	}

	// 处理返回结果
	glog.Println("status=%d resp=%s", result.Response.StatusCode, resp)

	if result.Response.StatusCode != 200 {
		glog.Println(result.Response)
	}

	// 将支付记录写入数据库
	_, err = g.DB().Model(dao.WxPayOrder.Table).Insert(model.WxPayOrder{
		OutTradeNo:                orderNumber,
		WxOrderNo:                 "",
		PayAmount:                 payInfo.BuySamplesCost,
		TaskName:                  taskInfo.TaskName,
		PayReason:                 1,
		TalentId:                  talentInfo.Id,
		TalentWxNickname:          talentInfo.TalentWxNickname,
		TalentPlatformAccountName: payInfo.PlatformNickname,
		TaskId:                    taskInfo.TaskId,
		TaskPlatform:              taskInfo.TaskPlatform,
		Success:                   0,
		CreateAt:                  gtime.Now(),
		Desc:                      "",
	})

	if err != nil {
		return &TalentHttpResult{Code: -13, Msg: "insert wx pay order failed"}
	}

	return &TalentHttpResult{Code: 0, Msg: "success", Data: talent_model.WxPrepayResponseData{
		OutTradeNo: orderNumber,
		WxResp: resp,
	}}
}

func WxPayQueryOrderById(r *ghttp.Request) *TalentHttpResult {

	transactionId := r.GetRequestString("transaction_id", "nil")
	if transactionId == "nil" {
		glog.Error("not input transaction_id param")
		return &TalentHttpResult{Code: -2, Msg: "input transaction_id param"}
	}


	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := wxUtils.LoadPrivateKeyWithPath(keyFilePath)
	if err != nil {
		glog.Error("load merchant private key error")
		return &TalentHttpResult{Code: -3, Msg: "load merchant private key error"}
	}

	ctx := context.Background()
	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		glog.Error("new wechat pay client err:%s", err)
		return &TalentHttpResult{Code: -4, Msg: "load merchant private key error"}
	}

	svc := jsapi.JsapiApiService{Client: client}
	resp, _, err := svc.QueryOrderById(ctx,
		jsapi.QueryOrderByIdRequest{
			TransactionId: core.String(transactionId),
			Mchid:         core.String(mchID),
		},
	)

	if err != nil {
		// 处理错误
		glog.Error("call WxPayQueryOrderById err:%s", err)
		return &TalentHttpResult{Code: -5, Msg: "call WxPayQueryOrderById failed"}
	}

	// TODO:支付结果写入数据库
	_, err = g.DB().Model(dao.WxPayOrder.Table).Update(g.Map{
		dao.WxPayOrder.Columns.WxOrderNo: resp.TransactionId,
		dao.WxPayOrder.Columns.Success: 1,
		dao.WxPayOrder.Columns.Desc: resp.TradeStateDesc,
	}, dao.WxPayOrder.Columns.OutTradeNo, resp.OutTradeNo)
	if err != nil {
		glog.Error("wxpay_handler.WxPayQueryOrderById Update database failed, order number = ", resp.OutTradeNo)
		return &TalentHttpResult{Code: -6, Msg: "Update database failed"}
	}

	return &TalentHttpResult{Code: 0, Msg: "success"}
}

func QueryOrderByOutTradeNo(r *ghttp.Request) *TalentHttpResult {
	outTradeNo := r.GetRequestString("out_trade_no", "nil")
	if outTradeNo == "nil" {
		glog.Error("not input out_trade_no param")
		return &TalentHttpResult{Code: -2, Msg: "input transaction_id param"}
	}

	g.DB().Model()
	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := wxUtils.LoadPrivateKeyWithPath(keyFilePath)
	if err != nil {
		glog.Error("load merchant private key error")
		return &TalentHttpResult{Code: -3, Msg: "load merchant private key error"}
	}

	ctx := context.Background()
	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		glog.Error("new wechat pay client err:%s", err)
		return &TalentHttpResult{Code: -4, Msg: "load merchant private key error"}
	}

	svc := jsapi.JsapiApiService{Client: client}
	resp, _, err := svc.QueryOrderByOutTradeNo(ctx,
		jsapi.QueryOrderByOutTradeNoRequest{
			OutTradeNo: core.String(outTradeNo),
			Mchid:      core.String(mchID),
		},
	)

	if err != nil {
		// 处理错误
		glog.Error("call QueryOrderByOutTradeNo err:%s", err)
		return &TalentHttpResult{Code: -5, Msg: "call QueryOrderByOutTradeNo failed"}
	}

	// TODO:支付结果写入数据库
	_, err = g.DB().Model(dao.WxPayOrder.Table).Update(g.Map{
		dao.WxPayOrder.Columns.WxOrderNo: resp.TransactionId,
		dao.WxPayOrder.Columns.Success: 1,
		dao.WxPayOrder.Columns.Desc: resp.TradeStateDesc,
	}, dao.WxPayOrder.Columns.OutTradeNo, resp.OutTradeNo)
	if err != nil {
		glog.Error("wxpay_handler.WxPayQueryOrderById Update database failed, order number = ", resp.OutTradeNo)
		return &TalentHttpResult{Code: -6, Msg: "Update database failed"}
	}

	return &TalentHttpResult{Code: 0, Msg: "success"}
}

func OnWxPayNotify(r *ghttp.Request)  {

	wxSignature := r.GetHeader("Wechatpay-Signature")
	if wxSignature == "" {
		glog.Error("OnWxPayNotify header not contain Wechatpay-Signature property")

		r.Response.WriteJsonExit(g.Map{
			"code": "FAILED",
			"message": "header中不包括Wechatpay-Signature",
		})
	}

}