-- Migration: Create Inbox table for event-driven async coupling
-- Purpose: Deduplication and idempotent consumption of outbox events
-- PIT Safety: All records are immutable (append-only)

CREATE TABLE outbox.inbox (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Foreign key to event
    outbox_id UUID NOT NULL,

    -- Consumer identification (e.g., "SignalR", "ApprovalQueue", "AuditLog")
    consumer_id VARCHAR(256) NOT NULL,

    -- Event metadata
    event_type VARCHAR(256) NOT NULL,
    payload JSONB NOT NULL,

    -- Processing status
    status VARCHAR(32) NOT NULL DEFAULT 'Pending',  -- Pending, Processed, Failed
    error_message TEXT,

    -- Timestamps
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    attempted_at TIMESTAMP,
    processed_at TIMESTAMP,

    -- Constraints
    CONSTRAINT inbox_outbox_fk
        FOREIGN KEY (outbox_id) REFERENCES outbox.outbox(id) ON DELETE RESTRICT,

    -- Idempotency: Each consumer processes each event exactly once
    CONSTRAINT inbox_idempotency
        UNIQUE (outbox_id, consumer_id),

    -- Status constraint
    CONSTRAINT inbox_status_valid
        CHECK (status IN ('Pending', 'Processed', 'Failed'))
);

-- Indexes for fast lookup
CREATE INDEX inbox_status_idx ON outbox.inbox(status);
CREATE INDEX inbox_created_idx ON outbox.inbox(created_at DESC);
CREATE INDEX inbox_consumer_idx ON outbox.inbox(consumer_id);

-- Constraint: If processed, must have processed_at
CREATE OR REPLACE FUNCTION outbox.inbox_processed_check()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.status = 'Processed' AND NEW.processed_at IS NULL THEN
        RAISE EXCEPTION 'processed_at must be set when status = Processed';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER inbox_processed_check_trigger
BEFORE INSERT OR UPDATE ON outbox.inbox
FOR EACH ROW
EXECUTE FUNCTION outbox.inbox_processed_check();
