-- name: runtimedeploytask__claim_next :one
WITH candidate AS (
    SELECT t.run_id
    FROM runtime_deploy_tasks t
    WHERE (
        t.status = 'pending'
        OR (
            t.status = 'running'
            AND t.lease_until IS NOT NULL
            AND t.lease_until < NOW()
        )
        OR (
            t.status = 'running'
            AND t.lease_until IS NOT NULL
            AND t.updated_at < NOW() - ($3::text)::interval
        )
    )
      -- Serialize claims by resolved namespace when it is known.
      -- For pending full-env tasks namespace may still be empty, so fallback to slot_no
      -- to allow parallel claims across different slots of the same environment.
      AND NOT EXISTS (
          SELECT 1
          FROM runtime_deploy_tasks active
          WHERE active.run_id <> t.run_id
            AND active.status = 'running'
            AND active.lease_until IS NOT NULL
            AND active.lease_until >= NOW()
            AND active.target_env = t.target_env
            AND (
                (
                    NULLIF(active.namespace, '') IS NOT NULL
                    AND NULLIF(t.namespace, '') IS NOT NULL
                    AND active.namespace = t.namespace
                )
                OR (
                    (
                        NULLIF(active.namespace, '') IS NULL
                        OR NULLIF(t.namespace, '') IS NULL
                    )
                    AND active.slot_no = t.slot_no
                )
            )
      )
    ORDER BY t.updated_at ASC
    FOR UPDATE SKIP LOCKED
    LIMIT 1
)
UPDATE runtime_deploy_tasks t
SET
    status = 'running',
    lease_owner = $1,
    lease_until = NOW() + ($2::text)::interval,
    attempts = t.attempts + 1,
    last_error = NULL,
    started_at = COALESCE(t.started_at, NOW()),
    finished_at = NULL,
    updated_at = NOW()
FROM candidate
WHERE t.run_id = candidate.run_id
RETURNING
    t.run_id::text AS run_id,
    t.runtime_mode,
    t.namespace,
    t.target_env,
    t.slot_no,
    t.repository_full_name,
    t.services_yaml_path,
    t.build_ref,
    t.deploy_only,
    t.status,
    COALESCE(t.lease_owner, '') AS lease_owner,
    t.lease_until,
    t.attempts,
    COALESCE(t.last_error, '') AS last_error,
    COALESCE(t.result_namespace, '') AS result_namespace,
    COALESCE(t.result_target_env, '') AS result_target_env,
    t.cancel_requested_at,
    COALESCE(t.cancel_requested_by, '') AS cancel_requested_by,
    COALESCE(t.cancel_reason, '') AS cancel_reason,
    t.stop_requested_at,
    COALESCE(t.stop_requested_by, '') AS stop_requested_by,
    COALESCE(t.stop_reason, '') AS stop_reason,
    COALESCE(t.terminal_status_source, '') AS terminal_status_source,
    t.terminal_event_seq,
    t.created_at,
    t.updated_at,
    t.started_at,
    t.finished_at,
    COALESCE(t.logs_json, '[]'::jsonb) AS logs_json;
