package notification

import (
	"strings"
	"time"

	"github.com/google/uuid"
)

// Intent is the open shape every producer submits to Submit. The package
// keeps the type unexported by package-side names so that producer
// packages (lobby, runtime) can construct values from their own
// vocabulary and let the wiring layer translate them with a thin
// adapter.
//
// Kind must be a value from the catalog (`backend/README.md` §10).
// IdempotencyKey is required and feeds the UNIQUE constraint on
// `notifications (kind, idempotency_key)`. Recipients lists user_ids
// for kinds that fan out per user; admin-channel kinds carry an empty
// slice. Payload is the kind-specific data blob; it is persisted as
// JSON on `notifications.payload` and forwarded to email templates.
// DeviceSessionID, when non-nil, narrows a push route to a single
// device session — the runtime / auth callers may use it to target
// specific clients.
type Intent struct {
	Kind            string
	IdempotencyKey  string
	Recipients      []uuid.UUID
	Payload         map[string]any
	DeviceSessionID *uuid.UUID
}

// Notification mirrors a row in `backend.notifications`. The admin API
// returns it directly; Worker keeps a copy alongside each route to
// avoid a re-fetch per dispatch.
type Notification struct {
	NotificationID uuid.UUID
	Kind           string
	IdempotencyKey string
	UserID         *uuid.UUID
	Payload        map[string]any
	CreatedAt      time.Time
}

// Route mirrors a row in `backend.notification_routes`. Status keeps
// the value documented in `backend/README.md` §10; channel is `push`
// or `email`. ResolvedEmail / ResolvedLocale capture the recipient
// address pinned at materialisation time so a downstream account edit
// cannot retarget an in-flight email.
type Route struct {
	RouteID         uuid.UUID
	NotificationID  uuid.UUID
	Channel         string
	Status          string
	Attempts        int32
	MaxAttempts     int32
	NextAttemptAt   *time.Time
	LastAttemptAt   *time.Time
	LastError       string
	ResolvedEmail   string
	ResolvedLocale  string
	UserID          *uuid.UUID
	DeviceSessionID *uuid.UUID
	CreatedAt       time.Time
	UpdatedAt       time.Time
	PublishedAt     *time.Time
	DeadLetteredAt  *time.Time
	SkippedAt       *time.Time
}

// DeadLetter mirrors a row in `backend.notification_dead_letters`.
type DeadLetter struct {
	DeadLetterID   uuid.UUID
	NotificationID uuid.UUID
	RouteID        uuid.UUID
	ArchivedAt     time.Time
	Reason         string
}

// MalformedIntent mirrors a row in
// `backend.notification_malformed_intents`. payload is the producer-
// supplied blob (best effort JSON-encoded by Submit); reason records
// the validation failure that diverted it.
type MalformedIntent struct {
	ID         uuid.UUID
	ReceivedAt time.Time
	Payload    map[string]any
	Reason     string
}

// trimSpace is a small helper used across the package; pulling it out
// avoids duplicate `strings.TrimSpace` import chains in tiny files.
func trimSpace(s string) string { return strings.TrimSpace(s) }

// timePtr returns a pointer to the supplied time. Useful in struct
// literals where Postgres-nullable timestamptz fields are pointers.
func timePtr(t time.Time) *time.Time { return &t }
