package encrypt

import (
	"crypto/ecdsa"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"github.com/emmansun/gmsm/sm2"
	"github.com/emmansun/gmsm/smx509"
)

type KeyType string

const (
	Raw    KeyType = "raw"
	Hex            = "hex"
	Base64         = "base64"
	Pem            = "pem"
)

const publicPemFormat = `-----BEGIN PUBLIC KEY-----
%s
-----END PUBLIC KEY-----`

const privatePemFormat = `-----BEGIN PRIVATE KEY-----
%s
-----END PRIVATE KEY-----`

func ParsePublicKey(key string, kt KeyType) (*ecdsa.PublicKey, error) {
	if len(key) == 0 {
		return nil, nil
	}

	switch kt {
	case Raw:
		return sm2.NewPublicKey([]byte(key))
	case Hex:
		pubBytes, err := hex.DecodeString(key)
		if err != nil {
			return nil, err
		}
		return sm2.NewPublicKey(pubBytes)
	case Base64:
		pubBytes, err := base64.StdEncoding.DecodeString(key)
		if err != nil {
			return nil, err
		}
		return sm2.NewPublicKey(pubBytes)
	case Pem:
		pubPEM := fmt.Sprintf(publicPemFormat, key)
		block, _ := pem.Decode([]byte(pubPEM))
		if block == nil {
			return nil, fmt.Errorf("failed to parse PEM block containing the public key")
		}

		pub, err := smx509.ParsePKIXPublicKey(block.Bytes)
		if err != nil {
			return nil, err
		}

		if !sm2.IsSM2PublicKey(pub) {
			return nil, fmt.Errorf("not a SM2 public key")
		}

		if key, ok := pub.(*ecdsa.PublicKey); ok {
			return key, nil
		}

		return nil, fmt.Errorf("not a valid SM2 public key")
	default:
		return nil, nil
	}
}

func ParsePrivateKey(key string, kt KeyType) (*sm2.PrivateKey, error) {
	if len(key) == 0 {
		return nil, nil
	}

	switch kt {
	case Raw:
		return sm2.NewPrivateKey([]byte(key))
	case Hex:
		priBytes, err := hex.DecodeString(key)
		if err != nil {
			return nil, err
		}
		return sm2.NewPrivateKey(priBytes)
	case Base64:
		priBytes, err := base64.StdEncoding.DecodeString(key)
		if err != nil {
			return nil, err
		}
		return sm2.NewPrivateKey(priBytes)
	case Pem:
		priPEM := fmt.Sprintf(privatePemFormat, key)
		block, _ := pem.Decode([]byte(priPEM))
		if block == nil {
			return nil, fmt.Errorf("failed to parse PEM block containing the private key")
		}

		pri, err := smx509.ParsePKCS8PrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}

		if pk, ok := pri.(*sm2.PrivateKey); ok {
			return pk, nil
		}
		return nil, fmt.Errorf("not a valid SM2 private key")
	default:
		return nil, fmt.Errorf("unsupported key type")
	}
}

func PriToPub(priKey string) (string, error) {
	priBytes, err := hex.DecodeString(priKey)
	if err != nil {
		return "", err
	}
	priObj, err := sm2.NewPrivateKey(priBytes)
	if err != nil {
		return "", err
	}

	pubBytes, err := smx509.MarshalPKIXPublicKey(&priObj.PublicKey)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(pubBytes), nil
}
