package keys

import (
	"crypto"
	"crypto/ed25519"
	"crypto/rand"
	"encoding/json"
	"errors"

	"github.com/DataDog/go-tuf/data"
)

func init() {
	SignerMap.Store(data.KeySchemeEd25519, NewP256Signer)
	VerifierMap.Store(data.KeySchemeEd25519, NewP256Verifier)
}

func NewP256Signer() Signer {
	return &ed25519Signer{}
}

func NewP256Verifier() Verifier {
	return &ed25519Verifier{}
}

type ed25519Verifier struct {
	PublicKey data.HexBytes `json:"public"`
	key       *data.PublicKey
}

func (e *ed25519Verifier) Public() string {
	return string(e.PublicKey)
}

func (e *ed25519Verifier) Verify(msg, sig []byte) error {
	if !ed25519.Verify([]byte(e.PublicKey), msg, sig) {
		return errors.New("tuf: ed25519 signature verification failed")
	}
	return nil
}

func (e *ed25519Verifier) MarshalPublicKey() *data.PublicKey {
	return e.key
}

func (e *ed25519Verifier) UnmarshalPublicKey(key *data.PublicKey) error {
	e.key = key
	if err := json.Unmarshal(key.Value, e); err != nil {
		return err
	}
	if len(e.PublicKey) != ed25519.PublicKeySize {
		return errors.New("tuf: unexpected public key length for ed25519 key")
	}
	return nil
}

type Ed25519PrivateKeyValue struct {
	Public  data.HexBytes `json:"public"`
	Private data.HexBytes `json:"private"`
}

type ed25519Signer struct {
	ed25519.PrivateKey

	keyType       string
	keyScheme     string
	keyAlgorithms []string
}

func GenerateEd25519Key() (*ed25519Signer, error) {
	_, private, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return nil, err
	}
	if err != nil {
		return nil, err
	}
	return &ed25519Signer{
		PrivateKey:    ed25519.PrivateKey(data.HexBytes(private)),
		keyType:       data.KeyTypeEd25519,
		keyScheme:     data.KeySchemeEd25519,
		keyAlgorithms: data.HashAlgorithms,
	}, nil
}

func NewEd25519Signer(keyValue Ed25519PrivateKeyValue) *ed25519Signer {
	return &ed25519Signer{
		PrivateKey:    ed25519.PrivateKey(data.HexBytes(keyValue.Private)),
		keyType:       data.KeyTypeEd25519,
		keyScheme:     data.KeySchemeEd25519,
		keyAlgorithms: data.HashAlgorithms,
	}
}

func (e *ed25519Signer) SignMessage(message []byte) ([]byte, error) {
	return e.Sign(rand.Reader, message, crypto.Hash(0))
}

func (e *ed25519Signer) MarshalPrivateKey() (*data.PrivateKey, error) {
	valueBytes, err := json.Marshal(Ed25519PrivateKeyValue{
		Public:  data.HexBytes([]byte(e.PrivateKey.Public().(ed25519.PublicKey))),
		Private: data.HexBytes(e.PrivateKey),
	})
	if err != nil {
		return nil, err
	}
	return &data.PrivateKey{
		Type:       e.keyType,
		Scheme:     e.keyScheme,
		Algorithms: e.keyAlgorithms,
		Value:      valueBytes,
	}, nil
}

func (e *ed25519Signer) UnmarshalPrivateKey(key *data.PrivateKey) error {
	keyValue := &Ed25519PrivateKeyValue{}
	if err := json.Unmarshal(key.Value, keyValue); err != nil {
		return err
	}
	*e = ed25519Signer{
		PrivateKey:    ed25519.PrivateKey(data.HexBytes(keyValue.Private)),
		keyType:       key.Type,
		keyScheme:     key.Scheme,
		keyAlgorithms: key.Algorithms,
	}
	return nil
}

func (e *ed25519Signer) PublicData() *data.PublicKey {
	keyValBytes, _ := json.Marshal(ed25519Verifier{PublicKey: []byte(e.PrivateKey.Public().(ed25519.PublicKey))})
	return &data.PublicKey{
		Type:       e.keyType,
		Scheme:     e.keyScheme,
		Algorithms: e.keyAlgorithms,
		Value:      keyValBytes,
	}
}