-- +goose Up
-- deliveries holds one durable record per accepted logical mail delivery.
-- The (source, idempotency_key) UNIQUE constraint replaces the previous Redis
-- idempotency keyspace: the durable row IS the idempotency reservation.
-- next_attempt_at is populated for deliveries whose active attempt is due in
-- the future and drives the attempt scheduler's `FOR UPDATE SKIP LOCKED` pull.
CREATE TABLE deliveries (
    delivery_id                text PRIMARY KEY,
    resend_parent_delivery_id  text NOT NULL DEFAULT '',
    source                     text NOT NULL,
    status                     text NOT NULL,
    payload_mode               text NOT NULL,
    template_id                text NOT NULL DEFAULT '',
    locale                     text NOT NULL DEFAULT '',
    locale_fallback_used       boolean NOT NULL DEFAULT false,
    template_variables         jsonb,
    attachments                jsonb,
    subject                    text NOT NULL DEFAULT '',
    text_body                  text NOT NULL DEFAULT '',
    html_body                  text NOT NULL DEFAULT '',
    idempotency_key            text NOT NULL,
    request_fingerprint        text NOT NULL,
    idempotency_expires_at     timestamptz NOT NULL,
    attempt_count              integer NOT NULL DEFAULT 0,
    last_attempt_status        text NOT NULL DEFAULT '',
    provider_summary           text NOT NULL DEFAULT '',
    next_attempt_at            timestamptz,
    created_at                 timestamptz NOT NULL,
    updated_at                 timestamptz NOT NULL,
    sent_at                    timestamptz,
    suppressed_at              timestamptz,
    failed_at                  timestamptz,
    dead_lettered_at           timestamptz,
    CONSTRAINT deliveries_idempotency_unique UNIQUE (source, idempotency_key)
);

-- Drives the scheduler's due-attempt pull. The partial predicate keeps the
-- index narrow: rows in terminal status (sent/suppressed/failed/dead_letter)
-- never appear here.
CREATE INDEX deliveries_due_idx
    ON deliveries (next_attempt_at)
    WHERE next_attempt_at IS NOT NULL;

-- Drives the recovery pass (deliveries currently held by an in-progress
-- attempt whose worker may have crashed).
CREATE INDEX deliveries_sending_idx
    ON deliveries (status)
    WHERE status = 'sending';

-- Newest-first listing index used by the operator delivery list surface.
CREATE INDEX deliveries_listing_idx
    ON deliveries (created_at DESC, delivery_id DESC);

-- Coarse status / source / template filters used by the operator listing.
CREATE INDEX deliveries_status_idx        ON deliveries (status);
CREATE INDEX deliveries_source_idx        ON deliveries (source);
CREATE INDEX deliveries_template_id_idx   ON deliveries (template_id) WHERE template_id <> '';

-- delivery_recipients normalises the SMTP envelope so future recipient-
-- filtered listing slots in without touching the deliveries row layout.
-- 'reply_to' addresses are stored for round-trip fidelity but excluded from
-- the email index per the prior keyspace rule.
CREATE TABLE delivery_recipients (
    delivery_id  text NOT NULL REFERENCES deliveries(delivery_id) ON DELETE CASCADE,
    kind         text NOT NULL,
    position     integer NOT NULL,
    email        text NOT NULL,
    PRIMARY KEY (delivery_id, kind, position),
    CONSTRAINT delivery_recipients_kind_check
        CHECK (kind IN ('to', 'cc', 'bcc', 'reply_to'))
);

CREATE INDEX delivery_recipients_email_idx
    ON delivery_recipients (email)
    WHERE kind <> 'reply_to';

-- attempts stores the immutable execution history of one delivery. attempt_no
-- is monotonically increasing per delivery, starting at 1.
CREATE TABLE attempts (
    delivery_id              text NOT NULL REFERENCES deliveries(delivery_id) ON DELETE CASCADE,
    attempt_no               integer NOT NULL,
    status                   text NOT NULL,
    scheduled_for            timestamptz NOT NULL,
    started_at               timestamptz,
    finished_at              timestamptz,
    provider_classification  text NOT NULL DEFAULT '',
    provider_summary         text NOT NULL DEFAULT '',
    PRIMARY KEY (delivery_id, attempt_no)
);

-- dead_letters holds the operator-visible record for one delivery that
-- exhausted automated handling.
CREATE TABLE dead_letters (
    delivery_id            text PRIMARY KEY REFERENCES deliveries(delivery_id) ON DELETE CASCADE,
    final_attempt_no       integer NOT NULL,
    failure_classification text NOT NULL,
    provider_summary       text NOT NULL DEFAULT '',
    recovery_hint          text NOT NULL DEFAULT '',
    created_at             timestamptz NOT NULL
);

-- delivery_payloads stores the raw generic-delivery attachment bundle
-- referenced by the delivery row. The payload column carries the
-- acceptgenericdelivery.DeliveryPayload JSON shape; raw attachment bytes
-- remain inside that JSON value as base64 strings.
CREATE TABLE delivery_payloads (
    delivery_id  text PRIMARY KEY REFERENCES deliveries(delivery_id) ON DELETE CASCADE,
    payload      jsonb NOT NULL
);

-- malformed_commands stores operator-visible records for stream commands the
-- intake validator could not accept.
CREATE TABLE malformed_commands (
    stream_entry_id   text PRIMARY KEY,
    delivery_id       text NOT NULL DEFAULT '',
    source            text NOT NULL DEFAULT '',
    idempotency_key   text NOT NULL DEFAULT '',
    failure_code      text NOT NULL,
    failure_message   text NOT NULL,
    raw_fields        jsonb NOT NULL,
    recorded_at       timestamptz NOT NULL
);

-- Newest-first listing index used by the operator malformed-command list.
CREATE INDEX malformed_commands_listing_idx
    ON malformed_commands (recorded_at DESC, stream_entry_id DESC);

-- +goose Down
DROP TABLE IF EXISTS malformed_commands;
DROP TABLE IF EXISTS delivery_payloads;
DROP TABLE IF EXISTS dead_letters;
DROP TABLE IF EXISTS attempts;
DROP TABLE IF EXISTS delivery_recipients;
DROP TABLE IF EXISTS deliveries;
