-- Saga pattern for resilient multi-step workflows
-- Sagas track multi-step operations with retry and compensation support

-- +goose Up

-- Main sagas table
CREATE TABLE sagas (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending',
    definition TEXT,
    vars JSONB NOT NULL DEFAULT '{}',
    outputs JSONB NOT NULL DEFAULT '{}',
    current_step TEXT,
    retry_count INT NOT NULL DEFAULT 0,
    max_retries INT NOT NULL DEFAULT 3,
    error TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    completed_at TIMESTAMPTZ,

    -- Constraints
    CONSTRAINT valid_status CHECK (status IN ('pending', 'running', 'completed', 'failed', 'compensating', 'compensated'))
);

-- Saga steps table
CREATE TABLE saga_steps (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    saga_id UUID NOT NULL REFERENCES sagas(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'pending',
    action TEXT NOT NULL,
    depends_on TEXT[] NOT NULL DEFAULT '{}',
    retry_policy JSONB NOT NULL DEFAULT '{"max_attempts": 3, "backoff_type": "exponential", "initial_delay": "5s", "max_delay": "60s"}',
    compensate TEXT,
    config JSONB NOT NULL DEFAULT '{}',
    output JSONB,
    error TEXT,
    retry_count INT NOT NULL DEFAULT 0,
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,

    -- Constraints
    CONSTRAINT unique_step_per_saga UNIQUE (saga_id, name),
    CONSTRAINT valid_step_status CHECK (status IN ('pending', 'running', 'completed', 'failed', 'skipped'))
);

-- Indexes for common queries
CREATE INDEX idx_sagas_status ON sagas(status) WHERE status IN ('pending', 'running', 'compensating');
CREATE INDEX idx_sagas_name ON sagas(name);
CREATE INDEX idx_sagas_created_at ON sagas(created_at DESC);
CREATE INDEX idx_saga_steps_saga_id ON saga_steps(saga_id);
CREATE INDEX idx_saga_steps_status ON saga_steps(saga_id, status);

-- Update timestamp trigger
CREATE OR REPLACE FUNCTION update_saga_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_saga_updated_at
    BEFORE UPDATE ON sagas
    FOR EACH ROW
    EXECUTE FUNCTION update_saga_updated_at();

-- +goose Down
DROP TRIGGER IF EXISTS trigger_saga_updated_at ON sagas;
DROP FUNCTION IF EXISTS update_saga_updated_at();
DROP INDEX IF EXISTS idx_saga_steps_status;
DROP INDEX IF EXISTS idx_saga_steps_saga_id;
DROP INDEX IF EXISTS idx_sagas_created_at;
DROP INDEX IF EXISTS idx_sagas_name;
DROP INDEX IF EXISTS idx_sagas_status;
DROP TABLE IF EXISTS saga_steps;
DROP TABLE IF EXISTS sagas;
