package protocol

import (
	"fmt"

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

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

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

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

	return NewSenderKeyMessageFromStruct(senderKeyMessageStructure, serializer)
}

// NewSenderKeyMessageFromStruct returns a Signal Ciphertext message from the
// given serializable structure.
func NewSenderKeyMessageFromStruct(structure *SenderKeyMessageStructure,
	serializer SenderKeyMessageSerializer) (*SenderKeyMessage, 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 message)", 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 message)", signalerror.ErrUnknownMessageVersion, structure.Version)
	}

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

	// Create the signal message object from the structure.
	whisperMessage := &SenderKeyMessage{
		keyID:      structure.ID,
		version:    structure.Version,
		iteration:  structure.Iteration,
		ciphertext: structure.CipherText,
		signature:  structure.Signature,
		serializer: serializer,
	}

	return whisperMessage, nil
}

// NewSenderKeyMessage returns a SenderKeyMessage.
func NewSenderKeyMessage(keyID uint32, iteration uint32, ciphertext []byte,
	signatureKey ecc.ECPrivateKeyable, serializer SenderKeyMessageSerializer) *SenderKeyMessage {

	// Ensure we have a valid signature key
	if signatureKey == nil {
		panic("Signature is nil. Unable to sign new senderkey message.")
	}

	// Build our SenderKeyMessage.
	senderKeyMessage := &SenderKeyMessage{
		keyID:      keyID,
		iteration:  iteration,
		ciphertext: ciphertext,
		version:    CurrentVersion,
		serializer: serializer,
	}

	// Sign the serialized message and include it in the message. This will be included
	// in the signed serialized version of the message.
	signature := ecc.CalculateSignature(signatureKey, senderKeyMessage.Serialize())
	senderKeyMessage.signature = bytehelper.ArrayToSlice64(signature)

	return senderKeyMessage
}

// SenderKeyMessageStructure is a serializeable structure for SenderKey messages.
type SenderKeyMessageStructure struct {
	ID         uint32
	Iteration  uint32
	CipherText []byte
	Version    uint32
	Signature  []byte
}

// SenderKeyMessage is a structure for messages using senderkey groups.
type SenderKeyMessage struct {
	version    uint32
	keyID      uint32
	iteration  uint32
	ciphertext []byte
	signature  []byte
	serializer SenderKeyMessageSerializer
}

// KeyID returns the SenderKeyMessage key ID.
func (p *SenderKeyMessage) KeyID() uint32 {
	return p.keyID
}

// Iteration returns the SenderKeyMessage iteration.
func (p *SenderKeyMessage) Iteration() uint32 {
	return p.iteration
}

// Ciphertext returns the SenderKeyMessage encrypted ciphertext.
func (p *SenderKeyMessage) Ciphertext() []byte {
	return p.ciphertext
}

// Version returns the Signal message version of the message.
func (p *SenderKeyMessage) Version() uint32 {
	return p.version
}

// Serialize will use the given serializer to return the message as bytes
// excluding the signature. This should be used for signing and verifying
// message signatures.
func (p *SenderKeyMessage) Serialize() []byte {
	structure := &SenderKeyMessageStructure{
		ID:         p.keyID,
		Iteration:  p.iteration,
		CipherText: p.ciphertext,
		Version:    p.version,
	}

	return p.serializer.Serialize(structure)
}

// SignedSerialize will use the given serializer to return the message as
// bytes with the message signature included. This should be used when
// sending the message over the network.
func (p *SenderKeyMessage) SignedSerialize() []byte {
	structure := &SenderKeyMessageStructure{
		ID:         p.keyID,
		Iteration:  p.iteration,
		CipherText: p.ciphertext,
		Version:    p.version,
		Signature:  p.signature,
	}

	return p.serializer.Serialize(structure)
}

// Signature returns the SenderKeyMessage signature
func (p *SenderKeyMessage) Signature() [64]byte {
	return bytehelper.SliceToArray64(p.signature)
}

// Type returns the sender key type.
func (p *SenderKeyMessage) Type() uint32 {
	return SENDERKEY_TYPE
}