package manager

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"strings"

	"chainweaver.org.cn/chainweaver/did/core"
	"chainweaver.org.cn/chainweaver/did/core/crypto"
	"chainweaver.org.cn/chainweaver/did/did-kms/kms"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/errorcode"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/config"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/db"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/logic/authority"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/middleware"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/svc"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/types"
	"chainweaver.org.cn/chainweaver/did/did-mgr-common-service/internal/utils"
	"chainweaver.org.cn/chainweaver/servicecommon/res/code"
	"google.golang.org/grpc/metadata"

	"github.com/zeromicro/go-zero/core/logx"
)

const OperatorCenterDID = "did:cndid:cndid"

type AddDidLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewAddDidLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddDidLogic {
	return &AddDidLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *AddDidLogic) AddDid(req *types.KeyPairReq) (resp *types.CommonResp, err error) {
	resp = &types.CommonResp{
		Code: types.SucceedCode,
		Msg:  types.SucceedMsg,
	}
	priKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
	if err != nil {
		errorcode.ParseErrorParsePrivateKeyFromPEM.BuildResult(resp, err)
		logx.Errorf("private key [%s] Parse err[%s]", req.PrivateKey, err.Error())
		return resp, nil
	}
	// 检查秘钥对是否有效,并计算address
	privateKey, err := crypto.ParsePrivateKeyFromPEM(priKey)
	if err != nil {
		errorcode.ParseErrorParsePrivateKeyFromPEM.BuildResult(resp, err)
		logx.Errorf("private key [%s] Parse err[%s]", req.PrivateKey, err.Error())
		return resp, nil
	}
	pkPem, _ := privateKey.PublicKey().String()
	address, _ := utils.ComputeEVMAddressFromPKPEM([]string{pkPem})

	// check did 是否已经绑定
	did, err := db.FindDidInfo()
	if err != nil && err.Error() != "record not found" {
		errorcode.MysqlErrorQuery.BuildResult(resp, err)
		logx.Errorf("did [%s] has exist", address[0])
		return resp, nil
	}
	if did != nil {
		errorcode.DidAlreadyExistError.BuildResult(resp)
		logx.Errorf("did has exist,dup add did")
		return resp, nil
	}

	//去运营端根据did address查询did,以及实名vc信息
	targetUrl := config.ConfigIns.Service.Did + utils.DIdGetDocumentByAddressUrl
	proxy := config.ConfigIns.Service.DidProxy
	docReq := &utils.DocumentGetByAddressRequest{Address: address[0]}
	document, err := utils.GetDocument(targetUrl, proxy, docReq)
	if err != nil {
		if strings.Contains(err.Error(), "record not found") {
			errorcode.DidIsNotExistError.BuildResult(resp)
			logx.Errorf("get [%s] vc by address of remote did service, err[%s]", address[0], err.Error())
			return resp, nil
		}
		errorcode.DocumentRequestError.BuildResult(resp, err)
		logx.Errorf("get [%s] document by address of remote did service, err[%s]", address[0], err.Error())
		return resp, nil
	}

	// 运营平台 did 必须是固定的
	if config.ConfigIns.IsOperatorCenter == 1 {
		if document.Id != OperatorCenterDID {
			err = fmt.Errorf("add operationCenter did failed,did id expected [%s],but get [%s]", OperatorCenterDID, document.Id)
			errorcode.OperationCenterErrorDid.BuildResult(resp, err)
			logx.Errorf(err.Error())
			return resp, nil
		}
	}
	//去远端拉取vc信息,并将vc信息存储
	proxy = config.ConfigIns.Service.DidProxy
	targetUrl = config.ConfigIns.Service.Did + utils.VcListUser
	vcByte, err := utils.GetRealNameVc(targetUrl, proxy, document.Id)
	if err != nil {
		errorcode.DocumentRequestError.BuildResult(resp, err)
		logx.Errorf("get [%s] vc by address of remote did service, err[%s]", address[0], err.Error())
		return resp, nil
	}
	vc := &core.VerifiableCredential{}
	enterAuthority := &authority.EnterpriseAuthority{}
	err = json.Unmarshal(vcByte.Vc, vc)
	if err != nil {
		err = fmt.Errorf("unmarshal vc failed, err[%s]", err.Error())
		errorcode.MarshalOrUnMarshalError.BuildResult(resp, err)
		l.Errorf(err.Error())
		return resp, nil
	}
	err = json.Unmarshal(vc.CredentialSubject, enterAuthority)
	if err != nil {
		err = fmt.Errorf("unmarshal enterAuthority failed, err[%s]", err.Error())
		errorcode.MarshalOrUnMarshalError.BuildResult(resp, err)
		l.Errorf(err.Error())
		return resp, nil
	}

	token := l.ctx.Value("claims").(*middleware.TokenUser)

	// 2. 记录到数据库
	opFromDate := utils.ParseAndFormatTime(enterAuthority.Opfrom)
	opToDate := utils.ParseAndFormatTime(enterAuthority.Opto)

	enterC := &db.EnterpriseCertInfo{
		EnterpriseID:   token.EnterpriseId,
		EnterpriseName: enterAuthority.Entname,
		Uniscid:        enterAuthority.Uniscid,
		LegalName:      enterAuthority.LegalName,
		Dom:            enterAuthority.Dom,
		LegalIDCard:    "",
		LegalPhone:     "",
		EnterCardType:  1,                      // 企业证件类型
		Opscope:        enterAuthority.Opscope, // 经营范围
		Opfrom:         opFromDate,
		Opto:           opToDate,
		Licencesn:      enterAuthority.Licencesn, // 电子营业执照序列号
		CardType:       1,
		VcPlaintext:    string(vcByte.Vc),
	}

	err = db.CreateTable(enterC)
	if err != nil {
		errorcode.MysqlErrorInsertInfo.BuildResult(resp, err)
		l.Errorf(err.Error())
		return resp, nil
	}

	enterInfo, err := db.FindEnterpriseInfoById(token.EnterpriseId)
	if err != nil {
		errorcode.MysqlErrorQuery.BuildResult(resp, err)
		l.Errorf(err.Error())
		return resp, nil
	}

	enterInfo.CertificationState = 1
	err = db.UpdateEnterpriseInfo(enterInfo)
	if err != nil {
		errorcode.MysqlErrorInsertInfo.BuildResult(resp, err)
		l.Errorf(err.Error())
		return resp, nil
	}

	req.PrivateKey = string(priKey)
	//保存私钥到kms
	resKey, err := l.AddKeyPair(req)
	if err != nil {
		errorcode.KmsAddDataError.BuildResult(resp, err)
		l.Logger.Info("kms add key failed,err[%s]", err.Error())
		return resp, nil
	}

	//2.local save
	err = db.CreateTable(&db.KeyInfo{
		Address:   resKey.Data,
		AdminName: token.UserName,
		State:     1,
		Enable:    1,
		Remark:    req.Mnemonic,
	})
	if err != nil {
		errorcode.MysqlErrorInsertInfo.BuildResult(resp, err)
		l.Logger.Info("create key info failed,err[%s]", err.Error())
		return resp, nil
	}

	docByte, err := json.Marshal(document)
	if err != nil {
		errorcode.MarshalOrUnMarshalError.BuildResult(resp, err)
		l.Errorf("marshal document failed,err[%s]", err.Error())
		return resp, nil
	}
	// 保存 did信息到kms
	_, err = AddDidInfo(l.svcCtx.KmsClient, docByte, document.Id)
	if err != nil {
		errorcode.KmsAddDataError.BuildResult(resp, err)
		l.Logger.Info("kms add did failed,err[%s]", err.Error())
		return resp, nil
	}
	// 3. 将did信息返回给客户端
	didInfo := &db.DidInfo{
		Did:               document.Id,
		DocumentPlaintext: string(docByte),
		EnterpriseID:      token.EnterpriseId,
	}
	err = db.CreateTable(didInfo)
	if err != nil {
		errorcode.MysqlErrorInsertInfo.BuildResult(resp, err)
		l.Errorf("create did info failed,err[%s]", err.Error())
		return resp, nil
	}

	return resp, nil
}

func (l *AddDidLogic) AddKeyPair(req *types.KeyPairReq) (*kms.AddKeyPairResponse, error) {

	in := &kms.AddKeyPairRequest{
		PrivateKey: req.PrivateKey,
		Mnemonic:   strings.Split(req.Mnemonic, " "),
	}

	// 在请求头中设置自定义参数
	ctx := context.Background()
	//md := metadata.Pairs("did", "my-value")
	//ctx = metadata.NewOutgoingContext(ctx, md)

	res, err := l.svcCtx.KmsClient.AddKeyPair(ctx, in)
	if err != nil {
		return nil, err
	} else if code.CodeOk.NotEqualsInt32(res.Code) {
		return nil, fmt.Errorf(res.Msg)
	}

	return res, err
}

func AddDidInfo(client kms.Kms, documentPlaintext []byte, did string) (*kms.AddDidResponse, error) {

	plaintext := base64.StdEncoding.EncodeToString(documentPlaintext)

	// 在请求头中设置自定义参数
	ctx := context.Background()
	md := metadata.Pairs("did", did)
	ctx = metadata.NewOutgoingContext(ctx, md)

	in := &kms.AddDidRequest{
		Plaintext: plaintext,
	}

	res, err := client.AddDid(ctx, in)
	if err != nil {
		return nil, err
	} else if code.CodeOk.NotEqualsInt32(res.Code) {
		return nil, fmt.Errorf(res.Msg)
	}

	return res, err
}