-- name: CreateWorkflowRun :one
INSERT INTO workflow_runs (
    id,
    project_id,
    workflow_version_id,
    status,
    trigger_type,
    input,
    idempotency_key
)
VALUES ($1, $2, $3, 'pending', $4, $5, $6)
RETURNING *;

-- name: CreateStepRun :one
INSERT INTO step_runs (
    id,
    project_id,
    workflow_run_id,
    step_key,
    executor_kind,
    max_attempts,
    priority,
    input,
    idempotency_key,
    available_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *;

-- name: LeaseNextStepRun :one
WITH candidate AS (
    SELECT queued.id
    FROM step_runs AS queued
    WHERE queued.executor_kind = sqlc.arg(requested_executor_kind)
      AND queued.status IN ('pending', 'retryable_failed')
      AND queued.available_at <= now()
      AND queued.attempt < queued.max_attempts
      AND (queued.leased_until IS NULL OR queued.leased_until < now())
    ORDER BY queued.priority DESC, queued.created_at
    FOR UPDATE SKIP LOCKED
    LIMIT 1
)
UPDATE step_runs AS step
SET status = 'leased',
    attempt = step.attempt + 1,
    leased_by = sqlc.arg(worker_id),
    leased_until = now() + make_interval(secs => sqlc.arg(lease_seconds)::integer),
    updated_at = now()
FROM candidate
WHERE step.id = candidate.id
RETURNING step.*;

-- name: MarkStepRunRunning :one
UPDATE step_runs
SET status = 'running',
    started_at = COALESCE(started_at, now()),
    updated_at = now()
WHERE id = $1
  AND leased_by = $2
  AND status = 'leased'
RETURNING *;

-- name: CompleteStepRun :one
UPDATE step_runs
SET status = 'succeeded',
    output = $3,
    leased_by = NULL,
    leased_until = NULL,
    finished_at = now(),
    updated_at = now()
WHERE id = $1
  AND leased_by = $2
  AND status IN ('leased', 'running')
RETURNING *;

-- name: FailStepRun :one
UPDATE step_runs
SET status = CASE
        WHEN attempt < max_attempts AND sqlc.arg(retryable)::boolean
            THEN 'retryable_failed'
        ELSE 'terminal_failed'
    END,
    error = sqlc.arg(error_payload),
    available_at = CASE
        WHEN attempt < max_attempts AND sqlc.arg(retryable)::boolean
            THEN now() + make_interval(secs => sqlc.arg(retry_delay_seconds)::integer)
        ELSE available_at
    END,
    leased_by = NULL,
    leased_until = NULL,
    finished_at = CASE
        WHEN attempt < max_attempts AND sqlc.arg(retryable)::boolean
            THEN NULL
        ELSE now()
    END,
    updated_at = now()
WHERE id = sqlc.arg(id)
  AND leased_by = sqlc.arg(worker_id)
  AND status IN ('leased', 'running')
RETURNING *;
