-- OpenMail initial schema. See ARCHITECTURE.md §3.
-- Native, agent-shaped data model (not Mox's per-account bbolt index).

CREATE EXTENSION IF NOT EXISTS pgcrypto;  -- gen_random_uuid()

-- Tenant isolation.
CREATE TABLE IF NOT EXISTS pods (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    name        text NOT NULL,
    created_at  timestamptz NOT NULL DEFAULT now()
);

-- Per-domain DKIM keys + DNS verification state.
CREATE TABLE IF NOT EXISTS domains (
    id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    pod_id          uuid NOT NULL REFERENCES pods(id) ON DELETE CASCADE,
    name            text NOT NULL,
    dkim_selector   text,
    dkim_privkey_ref text,            -- pointer to key in secret store; never the key itself
    verified        boolean NOT NULL DEFAULT false,
    created_at      timestamptz NOT NULL DEFAULT now(),
    UNIQUE (name)
);

-- An agent-owned address; first-class API resource.
CREATE TABLE IF NOT EXISTS inboxes (
    id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    pod_id        uuid NOT NULL REFERENCES pods(id) ON DELETE CASCADE,
    address       text NOT NULL,
    display_name  text,
    metadata      jsonb NOT NULL DEFAULT '{}'::jsonb,
    created_at    timestamptz NOT NULL DEFAULT now(),
    updated_at    timestamptz NOT NULL DEFAULT now(),
    UNIQUE (address)
);

CREATE TABLE IF NOT EXISTS threads (
    id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    inbox_id        uuid NOT NULL REFERENCES inboxes(id) ON DELETE CASCADE,
    subject         text,
    last_message_id uuid,
    message_count   integer NOT NULL DEFAULT 0,
    labels          text[] NOT NULL DEFAULT '{}',
    created_at      timestamptz NOT NULL DEFAULT now(),
    updated_at      timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS messages (
    id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    inbox_id        uuid NOT NULL REFERENCES inboxes(id) ON DELETE CASCADE,
    thread_id       uuid REFERENCES threads(id) ON DELETE SET NULL,
    message_id_hdr  text,                       -- RFC 5322 Message-ID
    in_reply_to     text,
    "references"    text[] NOT NULL DEFAULT '{}',
    from_addr       text,
    to_addrs        text[] NOT NULL DEFAULT '{}',
    cc              text[] NOT NULL DEFAULT '{}',
    bcc             text[] NOT NULL DEFAULT '{}',
    subject         text,
    preview         text,
    text            text,
    html            text,
    extracted_text  text,                       -- quoted history stripped
    extracted_html  text,
    raw_object_key  text,                       -- pointer to raw .eml in object store
    spf             text,                       -- inbound auth verdicts (from mox pkgs)
    dkim            text,
    dmarc           text,
    junk_score      real,
    labels          text[] NOT NULL DEFAULT '{}',
    size_bytes      bigint,
    headers         jsonb NOT NULL DEFAULT '{}'::jsonb,
    ts              tsvector,                   -- full-text search
    created_at      timestamptz NOT NULL DEFAULT now(),
    updated_at      timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS attachments (
    id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id    uuid NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    filename      text,
    content_type  text,
    size_bytes    bigint,
    object_key    text NOT NULL,
    inline        boolean NOT NULL DEFAULT false,
    content_id    text
);

CREATE TABLE IF NOT EXISTS drafts (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    inbox_id    uuid NOT NULL REFERENCES inboxes(id) ON DELETE CASCADE,
    thread_id   uuid REFERENCES threads(id) ON DELETE SET NULL,
    to_addrs    text[] NOT NULL DEFAULT '{}',
    cc          text[] NOT NULL DEFAULT '{}',
    bcc         text[] NOT NULL DEFAULT '{}',
    subject     text,
    text        text,
    html        text,
    send_at     timestamptz,
    client_id   text,
    created_at  timestamptz NOT NULL DEFAULT now()
);

-- Bearer tokens; only the hash is stored.
CREATE TABLE IF NOT EXISTS api_keys (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    pod_id      uuid NOT NULL REFERENCES pods(id) ON DELETE CASCADE,
    hash        bytea NOT NULL,
    scopes      text[] NOT NULL DEFAULT '{}',
    created_at  timestamptz NOT NULL DEFAULT now(),
    UNIQUE (hash)
);

CREATE TABLE IF NOT EXISTS webhooks (
    id           uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    pod_id       uuid NOT NULL REFERENCES pods(id) ON DELETE CASCADE,
    url          text NOT NULL,
    event_types  text[] NOT NULL DEFAULT '{}',
    secret       text NOT NULL,
    created_at   timestamptz NOT NULL DEFAULT now()
);

-- Outbound send queue + retries.
CREATE TABLE IF NOT EXISTS outbox (
    id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    message_id      uuid NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
    status          text NOT NULL DEFAULT 'queued',   -- queued|sending|sent|failed
    attempts        integer NOT NULL DEFAULT 0,
    last_error      text,
    next_attempt_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS events (
    id          uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    inbox_id    uuid REFERENCES inboxes(id) ON DELETE CASCADE,
    type        text NOT NULL,
    payload     jsonb NOT NULL DEFAULT '{}'::jsonb,
    created_at  timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_inboxes_pod          ON inboxes(pod_id);
CREATE INDEX IF NOT EXISTS idx_threads_inbox        ON threads(inbox_id);
CREATE INDEX IF NOT EXISTS idx_messages_thread      ON messages(thread_id);
CREATE INDEX IF NOT EXISTS idx_messages_inbox_time  ON messages(inbox_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_messages_ts          ON messages USING gin(ts);
CREATE INDEX IF NOT EXISTS idx_messages_labels      ON messages USING gin(labels);
CREATE INDEX IF NOT EXISTS idx_threads_labels       ON threads USING gin(labels);
CREATE INDEX IF NOT EXISTS idx_outbox_due           ON outbox(next_attempt_at) WHERE status = 'queued';
