package messagingimp

import (
	"time"

	"github.com/google/uuid"
	"github.com/tech/sendico/pkg/merrors"
	gmessaging "github.com/tech/sendico/pkg/generated/gmessaging"
	"github.com/tech/sendico/pkg/model"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type EnvelopeImp struct {
	uid       uuid.UUID
	dateTime  time.Time
	data      []byte
	sender    string
	signature model.NotificationEvent
}

func (e *EnvelopeImp) GetTimeStamp() time.Time {
	return e.dateTime
}

func (e *EnvelopeImp) GetMessageId() uuid.UUID {
	return e.uid
}

func (e *EnvelopeImp) GetSender() string {
	return e.sender
}

func (e *EnvelopeImp) GetData() []byte {
	return e.data
}

func (e *EnvelopeImp) GetSignature() model.NotificationEvent {
	return e.signature
}

func (e *EnvelopeImp) Serialize() ([]byte, error) {
	if e.data == nil {
		return nil, merrors.Internal("Envelope data is not initialized")
	}
	msg := gmessaging.Envelope{
		Event: &gmessaging.NotificationEvent{
			Type:   e.signature.StringType(),
			Action: e.signature.StringAction(),
		},
		MessageData: e.data,
		Metadata: &gmessaging.EventMetadata{
			MessageId: e.uid.String(),
			Sender:    e.sender,
			Timestamp: timestamppb.New(e.dateTime),
		},
	}
	return proto.Marshal(&msg)
}

func (e *EnvelopeImp) Wrap(data []byte) ([]byte, error) {
	e.data = data
	return e.Serialize()
}

func DeserializeImp(data []byte) (*EnvelopeImp, error) {
	var envelope gmessaging.Envelope
	if err := proto.Unmarshal(data, &envelope); err != nil {
		return nil, err
	}

	var e EnvelopeImp
	var err error
	if e.uid, err = uuid.Parse(envelope.Metadata.MessageId); err != nil {
		return nil, err
	}

	if envelope.Metadata.Timestamp != nil {
		e.dateTime = envelope.Metadata.Timestamp.AsTime()
	} else {
		e.dateTime = time.Now()
	}

	if e.signature, err = model.StringToNotificationEvent(envelope.Event.Type, envelope.Event.Action); err != nil {
		return nil, err
	}

	e.data = envelope.MessageData
	e.sender = envelope.Metadata.Sender

	return &e, nil
}

func CreateEnvelopeImp(sender string, signature model.NotificationEvent) *EnvelopeImp {
	return &EnvelopeImp{
		dateTime:  time.Now(),
		sender:    sender,
		uid:       uuid.New(),
		signature: signature,
	}
}
