package service

import (
	"encoding/json"
	"errors"
	"strings"
	"time"
)

var ErrInvalidSESEvent = errors.New("invalid ses event notification")

type SESEventNotification struct {
	EventType  string                     `json:"eventType"`
	Mail       SESEventMail               `json:"mail"`
	Bounce     *SESBounceEvent            `json:"bounce,omitempty"`
	Complaint  *SESComplaintEvent         `json:"complaint,omitempty"`
	Delivery   *SESDeliveryEvent          `json:"delivery,omitempty"`
	Metadata   map[string]any             `json:"-"`
	RawPayload map[string]json.RawMessage `json:"-"`
}

type SESEventMail struct {
	Timestamp        time.Time           `json:"timestamp"`
	MessageID        string              `json:"messageId"`
	Source           string              `json:"source"`
	Destination      []string            `json:"destination"`
	Tags             map[string][]string `json:"tags,omitempty"`
	ConfigurationSet string              `json:"configurationSet,omitempty"`
}

type SESBounceEvent struct {
	BounceType        string `json:"bounceType,omitempty"`
	BounceSubType     string `json:"bounceSubType,omitempty"`
	ReportingMTA      string `json:"reportingMTA,omitempty"`
	RemoteMTAIP       string `json:"remoteMtaIp,omitempty"`
	FeedbackID        string `json:"feedbackId,omitempty"`
	FeedbackSourceArn string `json:"feedbackSourceArn,omitempty"`
}

type SESComplaintEvent struct {
	ComplaintFeedbackType string `json:"complaintFeedbackType,omitempty"`
	UserAgent             string `json:"userAgent,omitempty"`
	FeedbackID            string `json:"feedbackId,omitempty"`
}

type SESDeliveryEvent struct {
	Timestamp            time.Time `json:"timestamp"`
	ProcessingTimeMillis int64     `json:"processingTimeMillis,omitempty"`
	ReportingMTA         string    `json:"reportingMTA,omitempty"`
	SMTPResponse         string    `json:"smtpResponse,omitempty"`
	RemoteMTAIP          string    `json:"remoteMtaIp,omitempty"`
}

type snsEnvelope struct {
	Type    string `json:"Type"`
	Message string `json:"Message"`
}

func ParseSESEventNotification(body []byte) (*SESEventNotification, error) {
	body = []byte(strings.TrimSpace(string(body)))
	if len(body) == 0 {
		return nil, ErrInvalidSESEvent
	}

	if inner, ok := unwrapSNSMessage(body); ok {
		body = inner
	}

	var raw map[string]json.RawMessage
	if err := json.Unmarshal(body, &raw); err != nil {
		return nil, errors.Join(ErrInvalidSESEvent, err)
	}

	var event SESEventNotification
	if err := json.Unmarshal(body, &event); err != nil {
		return nil, errors.Join(ErrInvalidSESEvent, err)
	}
	if strings.TrimSpace(event.EventType) == "" {
		return nil, errors.Join(ErrInvalidSESEvent, errors.New("event type is required"))
	}
	if strings.TrimSpace(event.Mail.MessageID) == "" {
		return nil, errors.Join(ErrInvalidSESEvent, errors.New("mail message id is required"))
	}
	event.RawPayload = raw
	return &event, nil
}

func unwrapSNSMessage(body []byte) ([]byte, bool) {
	var envelope snsEnvelope
	if err := json.Unmarshal(body, &envelope); err != nil {
		return nil, false
	}
	if strings.TrimSpace(envelope.Message) == "" {
		return nil, false
	}
	switch strings.TrimSpace(envelope.Type) {
	case "Notification", "":
		return []byte(envelope.Message), true
	default:
		return nil, false
	}
}
