/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package sw

import (
	"crypto/ecdsa"
	"crypto/x509"
	"errors"
	"fmt"
	"reflect"

	"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/bccsp"
	"github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/bccsp/utils"

	cspx509 "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/bccsp/x509"
	bcx509 "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/bctls/x509"
	sm2 "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/bctls/tencentsm2"
)

type sm2PKIXPublicKeyImportOptsKeyImporter struct{}

func (*sm2PKIXPublicKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
	der, ok := raw.([]byte)
	if !ok {
		return nil, errors.New("Invalid raw material. Expected byte array.")
	}

	if len(der) == 0 {
		return nil, errors.New("Invalid raw. It must not be nil.")
	}

	lowLevelKey, err := DERToPublicKey(der)
	if err != nil {
		return nil, fmt.Errorf("Failed converting PKIX to sm2 public key [%s]", err)
	}

	sm2PK, ok := lowLevelKey.(*sm2.PublicKey)
	if !ok {
		return nil, errors.New("Failed casting to sm2 public key. Invalid raw material.")
	}

	return &sm2PublicKey{sm2PK}, nil
}

type sm2PrivateKeyImportOptsKeyImporter struct{}

func (*sm2PrivateKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
	der, ok := raw.([]byte)
	if !ok {
		return nil, errors.New("[SM2DERPrivateKeyImportOpts] Invalid raw material. Expected byte array.")
	}

	if len(der) == 0 {
		return nil, errors.New("[SM2DERPrivateKeyImportOpts] Invalid raw. It must not be nil.")
	}

	lowLevelKey, err := DERToPrivateKey(der)
	//lowLevelKey, err := sm2.ParseSm2PrivateKey(der)
	if err != nil {
		return nil, fmt.Errorf("Failed converting PKIX to sm2 public key [%s]", err)
	}

	sm2SK, ok := lowLevelKey.(*sm2.PrivateKey)
	if !ok {
		return nil, errors.New("Failed casting to sm2 private key. Invalid raw material.")
	}

	return &sm2PrivateKey{sm2SK}, nil
	//	return &sm2PrivateKey{lowLevelKey}, nil
}

type sm2GoPublicKeyImportOptsKeyImporter struct{}

func (*sm2GoPublicKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
	lowLevelKey, ok := raw.(*sm2.PublicKey)
	if !ok {
		return nil, errors.New("Invalid raw material. Expected *sm2.PublicKey.")
	}

	return &sm2PublicKey{lowLevelKey}, nil
}

type aes256ImportKeyOptsKeyImporter struct{}

func (*aes256ImportKeyOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (bccsp.Key, error) {
	aesRaw, ok := raw.([]byte)
	if !ok {
		return nil, errors.New("Invalid raw material. Expected byte array.")
	}

	if aesRaw == nil {
		return nil, errors.New("Invalid raw material. It must not be nil.")
	}

	if len(aesRaw) != 32 {
		return nil, fmt.Errorf("Invalid Key Length [%d]. Must be 32 bytes", len(aesRaw))
	}

	return &aesPrivateKey{aesRaw, false}, nil
}

type hmacImportKeyOptsKeyImporter struct{}

func (*hmacImportKeyOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (bccsp.Key, error) {
	aesRaw, ok := raw.([]byte)
	if !ok {
		return nil, errors.New("Invalid raw material. Expected byte array.")
	}

	if len(aesRaw) == 0 {
		return nil, errors.New("Invalid raw material. It must not be nil.")
	}

	return &aesPrivateKey{aesRaw, false}, nil
}

type ecdsaPKIXPublicKeyImportOptsKeyImporter struct{}

func (*ecdsaPKIXPublicKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (bccsp.Key, error) {
	der, ok := raw.([]byte)
	if !ok {
		return nil, errors.New("Invalid raw material. Expected byte array.")
	}

	if len(der) == 0 {
		return nil, errors.New("Invalid raw. It must not be nil.")
	}

	lowLevelKey, err := DERToPublicKey(der)
	if err != nil {
		return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err)
	}

	ecdsaPK, ok := lowLevelKey.(*ecdsa.PublicKey)
	if !ok {
		return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.")
	}

	return &ecdsaPublicKey{ecdsaPK}, nil
}

type ecdsaPrivateKeyImportOptsKeyImporter struct{}

func (*ecdsaPrivateKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (bccsp.Key, error) {
	der, ok := raw.([]byte)
	if !ok {
		return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw material. Expected byte array.")
	}

	if len(der) == 0 {
		return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw. It must not be nil.")
	}

	lowLevelKey, err := DERToPrivateKey(der)
	if err != nil {
		return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err)
	}

	ecdsaSK, ok := lowLevelKey.(*ecdsa.PrivateKey)
	if !ok {
		return nil, errors.New("Failed casting to ECDSA private key. Invalid raw material.")
	}

	return &ecdsaPrivateKey{ecdsaSK}, nil
}

type ecdsaGoPublicKeyImportOptsKeyImporter struct{}

func (*ecdsaGoPublicKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (bccsp.Key, error) {
	lowLevelKey, ok := raw.(*ecdsa.PublicKey)
	if !ok {
		return nil, errors.New("Invalid raw material. Expected *ecdsa.PublicKey.")
	}

	return &ecdsaPublicKey{lowLevelKey}, nil
}

type x509PublicKeyImportOptsKeyImporter struct {
	bccsp *CSP
}

func (ki *x509PublicKeyImportOptsKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
	cert, ok := raw.(*x509.Certificate)
	if ok {
		x509Cert := utils.ParseX509Certificate2Sm2(cert)
		//x509 certificate
		pk := x509Cert.PublicKey
		//should use key type to indenfify ecdsa and sm2, this problem should be solved in x509 certificate XXX
		if x509Cert.SignatureAlgorithm == cspx509.SHA256WithSM2 {
			switch pk.(type) {
			case *sm2.PublicKey:
				//logger.Info("1...sm2 key import...")
				return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.SM2GoPublicKeyImportOpts{})].KeyImport(
					//pk.(*sm2.PublicKey),
					pk,
					&bccsp.SM2GoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
			default:
				return nil, errors.New("sm2 x509 Certificate's public key type not recognized. Supported keys: [SM2]")
			}
		} else {
			switch pk.(type) {
			case *ecdsa.PublicKey:
				//logger.Info("...ecdsa key import...")
				return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.ECDSAGoPublicKeyImportOpts{})].KeyImport(
					pk,
					&bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
			case *sm2.PublicKey:
				//logger.Info("2...sm2 key import...")
				return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.SM2GoPublicKeyImportOpts{})].KeyImport(
					pk.(*sm2.PublicKey),
					&bccsp.SM2GoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
			default:
				//fmt.Println("x509 Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]")
				return nil, errors.New("Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]")
			}
		}

	} else {
		cert, ok := raw.(*cspx509.Sm2Certificate)
		if ok {
			pk := cert.PublicKey
			switch pk.(type) {
			case *sm2.PublicKey:
				//logger.Info("3...sm2 key import...")
				return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.ECDSAGoPublicKeyImportOpts{})].KeyImport(
					pk,
					&bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
			default:
				return nil, errors.New("Certificate's public key type not recognized. Supported keys: [SM2]")
			}

		}else {
			cert, ok := raw.(*bcx509.Certificate)
			if ok {
				pk := cert.PublicKey
				switch pk.(type) {
				case *ecdsa.PublicKey:
					//logger.Info("...ecdsa key import...")
					return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.ECDSAGoPublicKeyImportOpts{})].KeyImport(
						pk,
						&bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
				case *sm2.PublicKey:
					//logger.Info("2...sm2 key import...")
					return ki.bccsp.KeyImporters[reflect.TypeOf(&bccsp.SM2GoPublicKeyImportOpts{})].KeyImport(
						pk.(*sm2.PublicKey),
						&bccsp.SM2GoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
				default:
					//fmt.Println("x509 Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]")
					return nil, errors.New("Certificate's public key type not recognized. Supported keys: [ECDSA, RSA]")
				}
			}
		}
	}

	return nil, errors.New("Invalid raw material. Expected *x509.Certificate.")
}
