package protocol

import (
	"fmt"

	"go.mau.fi/libsignal/ecc"
	"go.mau.fi/libsignal/signalerror"
)

// SenderKeyDistributionMessageSerializer is an interface for serializing and deserializing
// SenderKeyDistributionMessages into bytes. An implementation of this interface should be
// used to encode/decode the object into JSON, Protobuffers, etc.
type SenderKeyDistributionMessageSerializer interface {
	Serialize(signalMessage *SenderKeyDistributionMessageStructure) []byte
	Deserialize(serialized []byte) (*SenderKeyDistributionMessageStructure, error)
}

// NewSenderKeyDistributionMessageFromBytes will return a Signal Ciphertext message from the given
// bytes using the given serializer.
func NewSenderKeyDistributionMessageFromBytes(serialized []byte,
	serializer SenderKeyDistributionMessageSerializer) (*SenderKeyDistributionMessage, error) {

	// Use the given serializer to decode the signal message.
	signalMessageStructure, err := serializer.Deserialize(serialized)
	if err != nil {
		return nil, err
	}

	return NewSenderKeyDistributionMessageFromStruct(signalMessageStructure, serializer)
}

// NewSenderKeyDistributionMessageFromStruct returns a Signal Ciphertext message from the
// given serializable structure.
func NewSenderKeyDistributionMessageFromStruct(structure *SenderKeyDistributionMessageStructure,
	serializer SenderKeyDistributionMessageSerializer) (*SenderKeyDistributionMessage, error) {

	// Throw an error if the given message structure is an unsupported version.
	if structure.Version <= UnsupportedVersion {
		return nil, fmt.Errorf("%w %d (sender key distribution)", signalerror.ErrOldMessageVersion, structure.Version)
	}

	// Throw an error if the given message structure is a future version.
	if structure.Version > CurrentVersion {
		return nil, fmt.Errorf("%w %d (sender key distribution)", signalerror.ErrUnknownMessageVersion, structure.Version)
	}

	// Throw an error if the structure is missing critical fields.
	if structure.SigningKey == nil || structure.ChainKey == nil {
		return nil, fmt.Errorf("%w (sender key distribution)", signalerror.ErrIncompleteMessage)
	}

	// Get the signing key object from bytes.
	signingKey, err := ecc.DecodePoint(structure.SigningKey, 0)
	if err != nil {
		return nil, err
	}

	// Create the signal message object from the structure.
	message := &SenderKeyDistributionMessage{
		id:           structure.ID,
		iteration:    structure.Iteration,
		chainKey:     structure.ChainKey,
		version:      structure.Version,
		signatureKey: signingKey,
		serializer:   serializer,
	}

	// Generate the ECC key from bytes.
	message.signatureKey, err = ecc.DecodePoint(structure.SigningKey, 0)
	if err != nil {
		return nil, err
	}

	return message, nil
}

// NewSenderKeyDistributionMessage returns a Signal Ciphertext message.
func NewSenderKeyDistributionMessage(id uint32, iteration uint32,
	chainKey []byte, signatureKey ecc.ECPublicKeyable,
	serializer SenderKeyDistributionMessageSerializer) *SenderKeyDistributionMessage {

	return &SenderKeyDistributionMessage{
		id:           id,
		iteration:    iteration,
		chainKey:     chainKey,
		signatureKey: signatureKey,
		serializer:   serializer,
	}
}

// SenderKeyDistributionMessageStructure is a serializeable structure for senderkey
// distribution messages.
type SenderKeyDistributionMessageStructure struct {
	ID         uint32
	Iteration  uint32
	ChainKey   []byte
	SigningKey []byte
	Version    uint32
}

// SenderKeyDistributionMessage is a structure for senderkey distribution messages.
type SenderKeyDistributionMessage struct {
	id           uint32
	iteration    uint32
	chainKey     []byte
	version      uint32
	signatureKey ecc.ECPublicKeyable
	serializer   SenderKeyDistributionMessageSerializer
}

// ID will return the message's id.
func (p *SenderKeyDistributionMessage) ID() uint32 {
	return p.id
}

// Iteration will return the message's iteration.
func (p *SenderKeyDistributionMessage) Iteration() uint32 {
	return p.iteration
}

// ChainKey will return the message's chain key in bytes.
func (p *SenderKeyDistributionMessage) ChainKey() []byte {
	return p.chainKey
}

// SignatureKey will return the message's signature public key
func (p *SenderKeyDistributionMessage) SignatureKey() ecc.ECPublicKeyable {
	return p.signatureKey
}

// Serialize will use the given serializer and return the message as
// bytes.
func (p *SenderKeyDistributionMessage) Serialize() []byte {
	structure := &SenderKeyDistributionMessageStructure{
		ID:         p.id,
		Iteration:  p.iteration,
		ChainKey:   p.chainKey,
		SigningKey: p.signatureKey.Serialize(),
		Version:    CurrentVersion,
	}
	return p.serializer.Serialize(structure)
}

// Type will return the message's type.
func (p *SenderKeyDistributionMessage) Type() uint32 {
	return SENDERKEY_DISTRIBUTION_TYPE
}