-- v1.1.1: dead_letters — design notes §4.
--
-- Async invocations that exhaust their retry policy land here. Each
-- row carries the original event payload verbatim plus the attempt
-- history so handlers (registered via `dead_letter` triggers) and the
-- dashboard can decide what to do.
--
-- Schema mirrors design notes §4. The CHECK constraint on
-- `resolution` enforces the closed vocabulary used by both the SDK
-- (`dead_letters::resolve(id, reason)`) and the recursion-stop rule
-- (`handler_failed`). Sync HTTP failures (`reply_to.is_some()`) never
-- land here — they're served via the inbox channel.
--
-- Indexes:
-- - partial index on unresolved rows: the dashboard's
--   unresolved-count badge query (`COUNT(*) WHERE app_id = $1 AND
--   resolved_at IS NULL`).
-- - GC index on `created_at`: the weekly retention sweep.

CREATE TABLE dead_letters (
    id                 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    app_id             UUID NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
    -- The outbox.id row that exhausted retries. The outbox row itself
    -- has been deleted at this point.
    original_event_id  UUID NOT NULL,
    source             TEXT NOT NULL,
    op                 TEXT NOT NULL,
    -- Nullable because direct admin replays may have no trigger row.
    trigger_id         UUID,
    script_id          UUID,
    payload            JSONB NOT NULL,
    attempt_count      INT  NOT NULL,
    first_attempt_at   TIMESTAMPTZ NOT NULL,
    last_attempt_at    TIMESTAMPTZ NOT NULL,
    last_error         TEXT NOT NULL,
    created_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    resolved_at        TIMESTAMPTZ,
    resolution         TEXT
        CHECK (resolution IN
            ('replayed', 'ignored', 'handled_by_script', 'handler_failed'))
);

-- Dashboard unresolved-count badge — partial index on the predicate
-- the query uses.
CREATE INDEX idx_dead_letters_app_unresolved
    ON dead_letters (app_id)
    WHERE resolved_at IS NULL;

-- GC sweep scans by creation time.
CREATE INDEX idx_dead_letters_gc ON dead_letters (created_at);
