package whisper

import (
	"crypto/ecdsa"
	"time"

	"github.com/ethereum/go-ethereum/crypto"
)

type Message struct {
	Flags     byte
	Signature []byte
	Payload   []byte
	Sent      int64

	To *ecdsa.PublicKey
}

func NewMessage(payload []byte) *Message {
	return &Message{Flags: 0, Payload: payload, Sent: time.Now().Unix()}
}

func (self *Message) hash() []byte {
	return crypto.Sha3(append([]byte{self.Flags}, self.Payload...))
}

func (self *Message) sign(key *ecdsa.PrivateKey) (err error) {
	self.Flags = 1
	self.Signature, err = crypto.Sign(self.hash(), key)
	return
}

func (self *Message) Recover() *ecdsa.PublicKey {
	defer func() { recover() }() // in case of invalid sig
	return crypto.SigToPub(self.hash(), self.Signature)
}

func (self *Message) Encrypt(to *ecdsa.PublicKey) (err error) {
	self.Payload, err = crypto.Encrypt(to, self.Payload)
	if err != nil {
		return err
	}

	return nil
}

func (self *Message) Bytes() []byte {
	return append([]byte{self.Flags}, append(self.Signature, self.Payload...)...)
}

type Opts struct {
	From   *ecdsa.PrivateKey
	To     *ecdsa.PublicKey
	Ttl    time.Duration
	Topics [][]byte
}

func (self *Message) Seal(pow time.Duration, opts Opts) (*Envelope, error) {
	if opts.From != nil {
		err := self.sign(opts.From)
		if err != nil {
			return nil, err
		}
	}

	if opts.To != nil {
		err := self.Encrypt(opts.To)
		if err != nil {
			return nil, err
		}
	}

	if opts.Ttl == 0 {
		opts.Ttl = DefaultTtl
	}

	envelope := NewEnvelope(opts.Ttl, opts.Topics, self)
	envelope.Seal(pow)

	return envelope, nil
}
