package mixrepoimpl

import (
	"context"
	"encoding/json"
	"errors"
	"time"
	v1 "voucher/api/v1"
	"voucher/internal/biz/bo"
	"voucher/internal/biz/mixrepos"
	"voucher/internal/biz/vo"
	"voucher/internal/conf"
	"voucher/internal/pkg/cmb"
)

type CmbMixRepoImpl struct {
	bc *conf.Bootstrap
}

func NewCmbMixRepoImpl(bc *conf.Bootstrap) mixrepos.CmbMixRepo {
	return &CmbMixRepoImpl{bc: bc}
}

func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest, funcName vo.CmbFuncName) (*v1.CmbOrderRequest, error) {
	bizStr, err := s.Verify(ctx, req, funcName)
	if err != nil {
		return nil, err
	}

	var resp *v1.CmbOrderRequest
	if err = json.Unmarshal([]byte(bizStr), &resp); err != nil {
		return nil, err
	}

	return resp, nil
}

func (s *CmbMixRepoImpl) ProductQueryVerify(ctx context.Context, req *v1.CmbRequest, funcName vo.CmbFuncName) (*v1.CmbQueryProductRequest, error) {
	bizStr, err := s.Verify(ctx, req, funcName)
	if err != nil {
		return nil, err
	}

	var resp *v1.CmbQueryProductRequest
	if err = json.Unmarshal([]byte(bizStr), &resp); err != nil {
		return nil, err
	}

	return resp, nil
}

func (s *CmbMixRepoImpl) Verify(_ context.Context, req *v1.CmbRequest, funcName vo.CmbFuncName) (string, error) {
	str := cmb.SortStructStr(req, funcName.GetValue())

	b, err := cmb.Verify(s.bc.Cmb.CmbSm2Puk, str, req.Sign)
	if err != nil {
		return "", err
	}

	if !b {
		return "", errors.New("签名验证失败")
	}

	bizStr, err := cmb.Decrypt(s.bc.Cmb.Sm2Prk, req.EncryptBody)
	if err != nil {
		return "", err
	}

	return bizStr, nil
}

func (s *CmbMixRepoImpl) GetRequest(_ context.Context, reqBo *bo.CmbRequestBo) (*v1.CmbRequest, error) {

	encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, reqBo.BizContent)
	if err != nil {
		return nil, err
	}

	req := &v1.CmbRequest{
		Mid:         s.bc.Cmb.Mid,
		Aid:         s.bc.Cmb.Aid,
		Date:        time.Now().Format("20060102150405"),
		Random:      string(cmb.RandomBytes(16)),
		KeyAlias:    s.bc.Cmb.KeyAlias,
		CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
		EncryptBody: encryptBody,
		Sign:        "",
	}

	str := cmb.SortStructStr(req, reqBo.FuncName.GetValue())

	sing, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, str)
	if err != nil {
		return nil, err
	}

	req.Sign = sing

	return req, nil
}

func (s *CmbMixRepoImpl) GetResponse(_ context.Context, reqBo *bo.CmbResponseBo) (*v1.CmbReply, error) {

	reply := &v1.CmbReply{
		RespCode:    reqBo.RespCode,
		RespMsg:     reqBo.RespMsg,
		Date:        time.Now().Format("20060102150405"),
		KeyAlias:    s.bc.Cmb.KeyAlias,
		CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
		EncryptBody: "",
		Sign:        "",
	}

	if len(reqBo.BizContent) > 0 {
		encryptBody, err := cmb.Encrypt(s.bc.Cmb.Sm2Puk, reqBo.BizContent)
		if err != nil {
			return nil, err
		}
		reply.EncryptBody = encryptBody
	}

	str := cmb.SortStructStr(reply, reqBo.FuncName.GetValue())

	sign, err := cmb.Sign(s.bc.Cmb.CmbSm2Pik, str)
	if err != nil {
		return nil, err
	}

	reply.Sign = sign

	return reply, nil
}
