package wechatrepoimpl

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
	"io"
	"net/http"
	"strings"
	"time"
	err2 "voucher/api/err"
	"voucher/internal/biz/bo"
	"voucher/internal/biz/businesserr"
	"voucher/internal/biz/vo"
	"voucher/internal/biz/wechatrepo"
	"voucher/internal/conf"
	"voucher/internal/data"
	"voucher/internal/pkg/helper"
	"voucher/internal/pkg/request"
	"voucher/internal/pkg/supplier/qixing"
)

// CpnRepoImpl .
// @link https://pay.weixin.qq.com/doc/v3/merchant/4012463767
type CpnRepoImpl struct {
	bc     *conf.Bootstrap
	Server data.Server
}

func NewCpnRepoImpl(bc *conf.Bootstrap) (wechatrepo.WechatCpnRepo, error) {
	server := data.Server{
		MchID:                      bc.Wechat.MchID,
		MchCertificateSerialNumber: bc.Wechat.MchCertificateSerialNumber,
		WechatPayPublicKeyID:       bc.Wechat.WechatPayPublicKeyID,
	}

	if err := server.Validate(); err != nil {
		return nil, err
	}

	return &CpnRepoImpl{bc: bc, Server: server}, nil
}

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

	client, err := data.GetClient(ctx, c.Server)
	if err != nil {
		return nil, err2.ErrorWechatFAIL(err.Error())
	}

	return client, err
}

func (c *CpnRepoImpl) bodyErr(_ context.Context, result *core.APIResult) error {

	// // 格式：{"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"}
	bodyBytes, err := io.ReadAll(result.Response.Body)
	if err != nil {
		return err2.ErrorWechatFAIL(fmt.Sprintf("读取微信错误返回body报错:%s", err.Error()))
	}

	var beer *businesserr.BusinessErr
	if err = json.Unmarshal(bodyBytes, &beer); err != nil {
		log.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(bodyBytes), err.Error())
		return err2.ErrorWechatFAIL(fmt.Sprintf("微信错误返回内容解析错误:%s", err.Error()))
	}

	return beer
}

func (c *CpnRepoImpl) Order(ctx context.Context, order *bo.OrderBo) (string, error) {

	req := cashcoupons.SendCouponRequest{
		OutRequestNo:      core.String(order.OrderNo),
		Appid:             core.String(order.AppID),
		Openid:            core.String(order.Account),
		StockId:           core.String(order.BatchNo),
		StockCreatorMchid: core.String(order.MerchantNo),
	}

	client, err := c.GetClient(ctx)
	if err != nil {
		return "", err
	}

	svc := cashcoupons.CouponApiService{Client: client}

	resp, result, err := svc.SendCoupon(ctx, req)

	if err != nil {

		if result.Response != nil && result.Response.Body != nil {
			return "", c.bodyErr(ctx, result)
		}

		return "", err
	}

	return *resp.CouponId, nil
}

func (c *CpnRepoImpl) Query(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error) {

	// todo 确认下，多笔立减金用普通立减金的接口查询也能查，结果是准确的吗

	// 福州启蒙 - 启星
	if order.MerchantNo == "1715349578" {
		//return c.QxQuery(ctx, order)
	}

	return c.LsxdQuery(ctx, order)
}

func (c *CpnRepoImpl) QxQuery(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error) {

	if order.ActivityId == "" {
		return 0, fmt.Errorf("商户号 %s 只支持多笔立减金查询", order.MerchantNo)
	}

	b := qixing.QxQueryReq{
		OrderNo:  order.OrderNo,
		CouponId: order.VoucherNo,
		OpenId:   order.Account,
	}

	var strToBeSigned strings.Builder

	kvRows := helper.SortStructJsonTag(b)
	for _, kv := range kvRows {
		if kv.Key == "sign" || kv.Value == "" {
			continue
		}
		strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
	}
	s := strToBeSigned.String() + "config.AppKey"

	b.Sign = helper.Md5(s)

	body, err := json.Marshal(b)
	if err != nil {
		return 0, err
	}

	isSuccess := func(code int) bool {
		return code == http.StatusOK
	}

	h := http.Header{
		"Content-Type": []string{"application/json"},
	}

	_, body, err = request.Post(
		ctx,
		c.bc.WechatNotifyMQ.RegisterTagUrl,
		body,
		request.WithHeaders(h),
		request.WithStatusCodeFunc(isSuccess),
		request.WithTimeout(time.Second*10),
	)
	if err != nil {
		return 0, err
	}

	var resp qixing.QxQueryResp
	if err := json.Unmarshal(body, &resp); err != nil {
		return 0, err
	}

	// 统一返回状态类型
	return resp.Data.CouponState.GetStatus()
}

func (c *CpnRepoImpl) LsxdQuery(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error) {

	// 需要判断是多笔立减金查询还是普通立减金查询，此处需要更正查询方式

	req := cashcoupons.QueryCouponRequest{
		CouponId: core.String(order.VoucherNo),
		Appid:    core.String(order.AppID),
		Openid:   core.String(order.Account),
	}

	client, err := c.GetClient(ctx)
	if err != nil {
		return 0, err
	}

	svc := cashcoupons.CouponApiService{Client: client}

	resp, result, err := svc.QueryCoupon(ctx, req)
	if err != nil {

		if result.Response != nil && result.Response.Body != nil {
			return 0, c.bodyErr(ctx, result)
		}

		return 0, err
	}

	cpnStatus := CpnStatus(*resp.Status)

	if cpnStatus.IsRevoked() {

	}

	return CpnStatus(*resp.Status).GetStatus()
}

func (c *CpnRepoImpl) QueryCoupon(ctx context.Context, orderWechat *bo.OrderBo) (*cashcoupons.Coupon, error) {

	req := cashcoupons.QueryCouponRequest{
		CouponId: core.String(orderWechat.VoucherNo),
		Appid:    core.String(orderWechat.AppID),
		Openid:   core.String(orderWechat.Account),
	}

	client, err := c.GetClient(ctx)
	if err != nil {
		return nil, err
	}

	svc := cashcoupons.CouponApiService{Client: client}

	resp, result, err := svc.QueryCoupon(ctx, req)
	if err != nil {

		if result.Response != nil && result.Response.Body != nil {
			return nil, c.bodyErr(ctx, result)
		}

		return nil, err
	}

	return resp, nil
}

func (c *CpnRepoImpl) QueryProduct(ctx context.Context, stockCreatorMchId, stockId string) (*cashcoupons.Stock, error) {

	if stockCreatorMchId == "" || stockId == "" {
		return nil, err2.ErrorWechatFAIL("商户号或批次号不能为空")
	}

	client, err := c.GetClient(ctx)
	if err != nil {
		return nil, err
	}

	req := cashcoupons.QueryStockRequest{
		StockId:           core.String(stockId),
		StockCreatorMchid: core.String(stockCreatorMchId),
	}

	svc := cashcoupons.StockApiService{Client: client}
	response, result, err := svc.QueryStock(ctx, req)

	if err != nil {

		if result.Response != nil && result.Response.Body != nil {
			return nil, c.bodyErr(ctx, result)
		}

		return nil, err
	}

	return response, nil
}

func (c *CpnRepoImpl) QueryCallback(ctx context.Context) (*cashcoupons.Callback, error) {

	client, err := c.GetClient(ctx)
	if err != nil {
		return nil, err
	}

	svc := cashcoupons.CallBackUrlApiService{Client: client}

	response, result, err := svc.QueryCallback(ctx, cashcoupons.QueryCallbackRequest{
		Mchid: core.String(c.bc.Wechat.MchID),
	})

	if err != nil {

		if result.Response != nil && result.Response.Body != nil {
			return nil, c.bodyErr(ctx, result)
		}

		return nil, err
	}

	return response, nil
}

func (c *CpnRepoImpl) SetCallback(ctx context.Context, url string) (*cashcoupons.SetCallbackResponse, error) {

	client, err := c.GetClient(ctx)
	if err != nil {
		return nil, err
	}

	svc := cashcoupons.CallBackUrlApiService{Client: client}

	response, result, err := svc.SetCallback(ctx, cashcoupons.SetCallbackRequest{
		Mchid:     core.String(c.bc.Wechat.MchID),
		NotifyUrl: core.String(url),
		Switch:    core.Bool(true),
	})

	if err != nil {

		if result.Response != nil && result.Response.Body != nil {
			return nil, c.bodyErr(ctx, result)
		}

		return nil, err
	}

	return response, nil
}

func (c *CpnRepoImpl) RegisterNotifyTag(ctx context.Context, stockID string) error {

	// 注册“刚哥那边”回调中心tag,一个批次只能注册一次，消费时根据不同的tag消费(区分测试/正式注册tag处理)
	h := http.Header{
		"Content-Type": []string{"application/json"},
	}

	b := struct {
		TagName string `json:"tag_name"`
		StockID string `json:"stock_id"`
	}{
		TagName: c.bc.WechatNotifyMQ.Tag,
		StockID: stockID,
	}

	body, err := json.Marshal(b)
	if err != nil {
		return err
	}

	isSuccess := func(code int) bool {
		return code == http.StatusOK
	}

	_, _, err = request.Post(
		ctx,
		c.bc.WechatNotifyMQ.RegisterTagUrl,
		body,
		request.WithHeaders(h),
		request.WithStatusCodeFunc(isSuccess),
		request.WithTimeout(time.Second*20),
	)
	if err != nil {
		return err
	}

	return nil
}
