package webhooks

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"math"
	"strings"
	"time"

	"source.offmarket.win/aleagle/eaglecast/models"
)

type lettermintNotif struct {
	ID        string `json:"id"`
	Event     string `json:"event"`
	Timestamp string `json:"timestamp"`
	Data      struct {
		MessageID string `json:"message_id"`
		Subject   string `json:"subject"`
		Recipient string `json:"recipient"`
		Response  *struct {
			StatusCode     int    `json:"status_code"`
			EnhancedStatus string `json:"enhanced_status_code"`
			Content        string `json:"content"`
		} `json:"response"`
		Metadata json.RawMessage `json:"metadata"`
		Tag      string          `json:"tag"`
	} `json:"data"`
}

// Lettermint handles bounce webhook notifications from Lettermint.
type Lettermint struct {
	hmacKey []byte
}

// NewLettermint returns a new Lettermint webhook handler.
func NewLettermint(key []byte) *Lettermint {
	return &Lettermint{hmacKey: key}
}

// ProcessBounce processes an incoming Lettermint webhook payload and returns a bounce object.
func (l *Lettermint) ProcessBounce(sig string, body []byte) ([]models.Bounce, error) {
	if len(l.hmacKey) == 0 {
		return nil, fmt.Errorf("webhook key is not configured")
	}

	// Parse the signature header: t={timestamp},v1={hex_signature}.
	ts, sigHex, err := parseLettermintSignature(sig)
	if err != nil {
		return nil, err
	}

	// Verify timestamp tolerance (300 seconds).
	if math.Abs(float64(time.Now().Unix()-ts)) > 300 {
		return nil, fmt.Errorf("signature timestamp expired")
	}

	// Decode the hex signature from the header.
	sigB, err := hex.DecodeString(strings.TrimSpace(sigHex))
	if err != nil {
		return nil, fmt.Errorf("invalid signature encoding: %v", err)
	}

	// Compute HMAC-SHA256 of "{timestamp}.{body}" and compare.
	mac := hmac.New(sha256.New, l.hmacKey)
	mac.Write([]byte(fmt.Sprintf("%d.%s", ts, body)))

	if !hmac.Equal(mac.Sum(nil), sigB) {
		return nil, fmt.Errorf("invalid signature")
	}

	var n lettermintNotif
	if err := json.Unmarshal(body, &n); err != nil {
		return nil, fmt.Errorf("error unmarshalling Lettermint notification: %v", err)
	}

	// Map event to bounce type.
	var typ string
	switch n.Event {
	case "message.hard_bounced":
		typ = models.BounceTypeHard
	case "message.soft_bounced":
		typ = models.BounceTypeSoft
	case "message.spam_complaint":
		typ = models.BounceTypeComplaint
	default:
		// Ignore irrelevant events (e.g. webhook.test).
		return nil, nil
	}

	campUUID := ""
	if len(n.Data.Metadata) > 0 {
		var meta map[string]string
		if err := json.Unmarshal(n.Data.Metadata, &meta); err == nil {
			if v, ok := meta["X-EagleCast-Campaign"]; ok {
				campUUID = v
			}
		}
	}

	t, _ := time.Parse(time.RFC3339, n.Timestamp)
	if t.IsZero() {
		t = time.Now()
	}

	return []models.Bounce{{
		Email:        strings.ToLower(n.Data.Recipient),
		CampaignUUID: campUUID,
		Type:         typ,
		Source:       "lettermint",
		Meta:         json.RawMessage(body),
		CreatedAt:    t,
	}}, nil
}

// parseLettermintSignature parses a signature header of the form "t={timestamp},v1={hex}".
func parseLettermintSignature(sig string) (int64, string, error) {
	var (
		ts   int64
		hash string
	)

	for _, part := range strings.Split(sig, ",") {
		kv := strings.SplitN(strings.TrimSpace(part), "=", 2)
		if len(kv) != 2 {
			continue
		}
		switch kv[0] {
		case "t":
			if _, err := fmt.Sscanf(kv[1], "%d", &ts); err != nil {
				return 0, "", fmt.Errorf("invalid timestamp in signature: %v", err)
			}
		case "v1":
			hash = kv[1]
		}
	}

	if ts == 0 || hash == "" {
		return 0, "", fmt.Errorf("invalid signature format")
	}

	return ts, hash, nil
}
