-- name: InsertSandbox :one
INSERT INTO sandboxes (id, team_id, host_id, template, status, vcpus, memory_mb, timeout_sec, disk_size_mb, template_id, template_team_id, metadata)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
RETURNING *;

-- name: GetSandbox :one
SELECT * FROM sandboxes WHERE id = $1;

-- name: GetSandboxByTeam :one
SELECT * FROM sandboxes WHERE id = $1 AND team_id = $2;

-- name: GetSandboxProxyTarget :one
-- Returns the sandbox status and its host's address in one query.
-- Used by SandboxProxyWrapper to avoid two round-trips.
SELECT s.status, h.address AS host_address
FROM sandboxes s
JOIN hosts h ON h.id = s.host_id
WHERE s.id = $1;

-- name: ListSandboxes :many
SELECT * FROM sandboxes ORDER BY created_at DESC;

-- name: ListSandboxesByTeam :many
SELECT * FROM sandboxes
WHERE team_id = $1 AND status NOT IN ('stopped', 'error')
ORDER BY created_at DESC;

-- name: ListSandboxesByHostAndStatus :many
SELECT * FROM sandboxes
WHERE host_id = $1 AND status = ANY($2::text[])
ORDER BY created_at DESC;

-- name: UpdateSandboxRunning :one
UPDATE sandboxes
SET status = 'running',
    host_ip = $2,
    guest_ip = $3,
    started_at = $4,
    last_active_at = $4,
    last_updated = NOW()
WHERE id = $1
RETURNING *;

-- name: UpdateSandboxStatus :one
UPDATE sandboxes
SET status = $2,
    last_updated = NOW()
WHERE id = $1
RETURNING *;

-- name: UpdateLastActive :exec
UPDATE sandboxes
SET last_active_at = $2,
    last_updated = NOW()
WHERE id = $1;

-- name: BulkUpdateStatusByIDs :exec
UPDATE sandboxes
SET status = $2,
    last_updated = NOW()
WHERE id = ANY($1::uuid[]);

-- name: ListActiveSandboxesByTeam :many
SELECT * FROM sandboxes
WHERE team_id = $1 AND status IN ('running', 'paused', 'starting', 'hibernated')
ORDER BY created_at DESC;

-- name: MarkSandboxesMissingByHost :exec
-- Called when the host monitor marks a host unreachable.
-- Marks running/starting/pending sandboxes on that host as 'missing' so users see
-- the sandbox is not currently reachable, without permanently losing the record.
UPDATE sandboxes
SET status       = 'missing',
    last_updated = NOW()
WHERE host_id = $1 AND status IN ('running', 'starting', 'pending', 'pausing', 'resuming', 'stopping');

-- name: UpdateSandboxMetadata :exec
UPDATE sandboxes
SET metadata = $2,
    last_updated = NOW()
WHERE id = $1;

-- name: UpdateSandboxRunningIf :one
-- Conditionally transition a sandbox to running only if the current status
-- matches the expected value. Prevents races where a user destroys a sandbox
-- while the create/resume goroutine is still in-flight.
UPDATE sandboxes
SET status = 'running',
    host_ip = $3,
    guest_ip = $4,
    started_at = $5,
    last_active_at = $5,
    last_updated = NOW()
WHERE id = $1 AND status = $2
RETURNING *;

-- name: UpdateSandboxStatusIf :one
-- Atomically update status only when the current status matches the expected value.
-- Prevents background goroutines from overwriting a status that has since changed
-- (e.g. user destroyed a sandbox while Create was in-flight).
UPDATE sandboxes
SET status       = $3,
    last_updated = NOW()
WHERE id = $1 AND status = $2
RETURNING *;

-- name: BulkRestoreMissingToStatus :exec
-- Called by the reconciler when a host comes back online and its sandboxes are
-- confirmed alive. Restores only sandboxes currently in 'missing' state to the
-- given target status (typically 'running' or 'paused' based on the live state
-- reported by the host agent's ListSandboxes RPC).
UPDATE sandboxes
SET status       = $2,
    last_updated = NOW()
WHERE id = ANY($1::uuid[]) AND status = 'missing';

-- name: UpdateSandboxDiskSize :exec
UPDATE sandboxes
SET disk_size_mb = $2,
    last_updated = NOW()
WHERE id = $1;
