-- 024: Transactional Outbox table (Outbox Pattern for guaranteed Kafka delivery)
-- Every service writes domain events to this table in the SAME transaction as the business data.
-- A separate relay process (OutboxRelay) polls this table and publishes to Kafka.
-- This guarantees exactly-once semantics: no event is lost, no event is duplicated.
--
-- Idempotency: consumers use (aggregate_id + event_id) as idempotency key.
-- Events expire after 24h (idempotency window).

CREATE TABLE IF NOT EXISTS outbox (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    aggregate_type VARCHAR(100) NOT NULL,       -- e.g. 'User', 'Coupon', 'Order', 'Trade'
    aggregate_id UUID NOT NULL,                 -- ID of the business entity
    event_type VARCHAR(100) NOT NULL,           -- e.g. 'user.registered', 'trade.matched'
    topic VARCHAR(100) NOT NULL,                -- Kafka topic name
    partition_key VARCHAR(100),                 -- Kafka partition key (for ordering)
    payload JSONB NOT NULL,                     -- Event payload
    headers JSONB DEFAULT '{}',                 -- Additional headers (traceId, source, etc.)
    status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'published', 'failed')),
    retry_count SMALLINT NOT NULL DEFAULT 0,
    max_retries SMALLINT NOT NULL DEFAULT 5,
    published_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '24 hours'),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Index for the relay poller: find pending events efficiently
CREATE INDEX idx_outbox_status_created ON outbox(status, created_at) WHERE status = 'pending';
-- Index for idempotency lookups
CREATE INDEX idx_outbox_aggregate ON outbox(aggregate_type, aggregate_id);
-- Index for cleanup of expired events
CREATE INDEX idx_outbox_expires ON outbox(expires_at) WHERE status = 'published';

-- Idempotency tracking: consumers record processed event IDs here
CREATE TABLE IF NOT EXISTS processed_events (
    event_id UUID PRIMARY KEY,
    consumer_group VARCHAR(100) NOT NULL,
    processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '24 hours')
);

CREATE INDEX idx_processed_events_consumer ON processed_events(consumer_group);
CREATE INDEX idx_processed_events_expires ON processed_events(expires_at);

-- Cleanup job: remove expired outbox entries and processed_events (run daily)
-- This keeps the tables lean while maintaining the 24h idempotency window.
