package jwt

// Taken from https://github.com/dgrijalva/jwt-go/blob/master/ecdsa_utils.go
import (
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
)

var (
	ErrNotECPublicKey      = errors.New("jwt: key is not a valid ECDSA public key")
	ErrNotECPrivateKey     = errors.New("jwt: key is not a valid ECDSA private key")
	ErrKeyMustBePEMEncoded = errors.New("jwt: key must be PEM encoded PKCS1 or PKCS8 private key")
)

// Parse PEM encoded Elliptic Curve Private Key Structure
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
	var err error

	// Parse PEM block
	var block *pem.Block
	if block, _ = pem.Decode(key); block == nil {
		return nil, ErrKeyMustBePEMEncoded
	}

	// Parse the key
	var parsedKey interface{}
	if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil {
		return nil, err
	}

	var pkey *ecdsa.PrivateKey
	var ok bool
	if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
		return nil, ErrNotECPrivateKey
	}

	return pkey, nil
}

// Parse PEM encoded PKCS1 or PKCS8 public key
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
	var err error

	// Parse PEM block
	var block *pem.Block
	if block, _ = pem.Decode(key); block == nil {
		return nil, ErrKeyMustBePEMEncoded
	}

	// Parse the key
	var parsedKey interface{}
	if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
		if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
			parsedKey = cert.PublicKey
		} else {
			return nil, err
		}
	}

	var pkey *ecdsa.PublicKey
	var ok bool
	if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok {
		return nil, ErrNotECPublicKey
	}

	return pkey, nil
}

// ReadECDSAPrivateKey reads a EC private key from a pem file
func ReadECDSAPrivateKey(privateKeyFile string) (*ecdsa.PrivateKey, error) {
	privKey, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		return nil, err
	}

	private, err := ParseECPrivateKeyFromPEM(privKey)
	if err != nil {
		return nil, err
	}

	return private, nil
}

// ReadECDSAPublicKey reads a EC public key from a pem file
func ReadECDSAPublicKey(publicKeyFile string) (*ecdsa.PublicKey, error) {
	pubKey, err := ioutil.ReadFile(publicKeyFile)
	if err != nil {
		return nil, err
	}

	public, err := ParseECPublicKeyFromPEM(pubKey)
	if err != nil {
		return nil, err
	}

	return public, nil
}

// ECDSAValidateOnly takes a function that takes a PrivateKey and returns an Algorithm that only allows validation
func ECDSAValidateOnly(f func(*ecdsa.PrivateKey) Algorithm, pub *ecdsa.PublicKey) Algorithm {
	return f(&ecdsa.PrivateKey{PublicKey: *pub})
}

// Generate ECDSA Private and Public key from pem files
func GetECDSAPrivateKey(privateKeyFile, publicKeyFile string) *ecdsa.PrivateKey {
	priv, _ := ReadECDSAPrivateKey(privateKeyFile)
	pub, _ := ReadECDSAPublicKey(publicKeyFile)

	priv.PublicKey = *pub
	return priv
}
