-- 007_clickhouse.up.sql
-- M7: ClickHouse schema for the deliveries archive.
-- M8: + deliveries_dlq_archive (DLQ) with 2y TTL.
--
-- ClickHouse is columnar and prefers wide tables with
-- per-column compression. The schema mirrors the Postgres
-- `deliveries` table one-to-one for v1; a future M7.5 can
-- strip unused fields (e.g. drop the JSONB payload blob
-- if we never query into it in CH).
--
-- The DDL below is designed to be runnable from the
-- archiverd's first-run setup hook. It's also what the
-- M7 smoke executes by hand via curl http://clickhouse:8123/.
--
-- Note on types:
--   - `id` is BIGINT in Postgres (BIGSERIAL). In CH we
--     keep BIGINT — CH's UInt64 is the closest match,
--     but for an archive that doesn't reference id
--     anywhere else, BIGINT is fine.
--   - `payload` is JSONB in Postgres. We store it as a
--     CH String. The Go side marshals to JSON text
--     before INSERT. CH's JSON type is still experimental
--     in 24.x; String is the safe choice.
--   - `status` is a small enum ('pending','sent','failed','dlq').
--     In CH we use LowCardinality(String) — the column
--     takes ~0.5 bytes per row for the dictionary.

CREATE DATABASE IF NOT EXISTS ba_archive;

-- ── M7: live deliveries archive ─────────────────────────────────
-- 1-year retention. Rows that age out of the Timescale
-- 7d hot window land here; after 1y CH drops the
-- partition. Operators can override via
--   ALTER TABLE ba_archive.deliveries_archive MODIFY TTL ...
CREATE TABLE IF NOT EXISTS ba_archive.deliveries_archive (
    id              BIGINT,
    alert_id        String,
    company_id      String,
    individual_id   String,
    channel         LowCardinality(String),
    target          String,
    status          LowCardinality(String),
    attempts        UInt32,
    last_error      String,
    payload         String,  -- raw JSON
    created_at      DateTime64(3, 'UTC'),
    sent_at         Nullable(DateTime64(3, 'UTC')),
    next_attempt_at Nullable(DateTime64(3, 'UTC')),
    -- The source postgres row's last-update marker. We
    -- set this to the wall-clock time at insert. Useful
    -- for "show me everything archived in the last 24h".
    archived_at     DateTime DEFAULT now()
) ENGINE = MergeTree
  PARTITION BY toYYYYMM(created_at)
  ORDER BY (company_id, created_at, id)
  TTL toDateTime(created_at) + INTERVAL 365 DAY;

-- A MATERIALIZED VIEW that aggregates per-company,
-- per-day delivery counts. Used by M9 dashboards.
CREATE MATERIALIZED VIEW IF NOT EXISTS
    ba_archive.deliveries_per_company_daily_mv
ENGINE = SummingMergeTree
  PARTITION BY toYYYYMM(day)
  ORDER BY (company_id, day, channel, status)
AS
SELECT
    company_id,
    toDate(created_at) AS day,
    channel,
    status,
    count()           AS n
FROM ba_archive.deliveries_archive
GROUP BY company_id, day, channel, status;

-- ── M8: DLQ archive ────────────────────────────────────────────
-- 2-year retention (vs 1y for live). DLQ entries are
-- forensic data — operators want them around longer
-- when triaging a regression. Mirrors the live
-- deliveries_archive column shape minus the retry-
-- scheduling columns (sent_at, next_attempt_at) which
-- the DLQ doesn't carry.
CREATE TABLE IF NOT EXISTS ba_archive.deliveries_dlq_archive (
    id              BIGINT,
    alert_id        String,
    company_id      String,
    individual_id   String,
    channel         LowCardinality(String),
    target          String,
    status          LowCardinality(String),
    attempts        UInt32,
    last_error      String,
    payload         String,  -- raw JSON, the original NATS envelope
    created_at      DateTime64(3, 'UTC'),
    archived_at     DateTime DEFAULT now()
) ENGINE = MergeTree
  PARTITION BY toYYYYMM(created_at)
  ORDER BY (company_id, created_at, id)
  TTL toDateTime(created_at) + INTERVAL 730 DAY;

-- Per-company daily DLQ counts. Used by the M9
-- observability stack to surface DLQ rate per tenant.
CREATE MATERIALIZED VIEW IF NOT EXISTS
    ba_archive.deliveries_dlq_per_company_daily_mv
ENGINE = SummingMergeTree
  PARTITION BY toYYYYMM(day)
  ORDER BY (company_id, day, channel)
AS
SELECT
    company_id,
    toDate(created_at) AS day,
    channel,
    count()           AS n
FROM ba_archive.deliveries_dlq_archive
GROUP BY company_id, day, channel;
