-- name: GetMostRecentPlaySessionForUser :one
SELECT * FROM play_sessions
WHERE user_id = $1
ORDER BY last_event_at DESC
LIMIT 1;

-- name: InsertPlaySession :one
INSERT INTO play_sessions (user_id, started_at, last_event_at, client_id)
VALUES ($1, $2, $2, $3)
RETURNING *;

-- name: TouchPlaySessionLastEvent :exec
UPDATE play_sessions
SET last_event_at = $2,
    track_count = track_count + 1
WHERE id = $1;

-- name: GetOpenPlayEventForUser :one
-- Returns the most recent play_event for a user where ended_at IS NULL.
-- Used by the auto-close-prior step in playevents.RecordPlayStarted.
SELECT * FROM play_events
WHERE user_id = $1 AND ended_at IS NULL
ORDER BY started_at DESC
LIMIT 1;

-- name: InsertPlayEvent :one
-- pick_kind is non-NULL only for system-playlist plays whose track was
-- found (with a stamped kind) in the user's live snapshot for that
-- variant at ingestion time (#1249, generalized in #1270).
INSERT INTO play_events (
    user_id, track_id, session_id, started_at, client_id, source, pick_kind,
    device_class
) VALUES ($1, $2, $3, $4, $5, $6, sqlc.narg(pick_kind)::text,
    sqlc.narg(device_class)::text)
RETURNING *;

-- name: GetSystemPickKindForTrack :one
-- Looks a track up in the user's CURRENT snapshot of the given system
-- variant and returns its pick_kind. Used at play-ingestion time to
-- freeze provenance onto the play_event — snapshots rebuild daily, so
-- attribution can't be reconstructed at read time (#1249/#1270). No
-- row = track not in today's snapshot (caller stores NULL); a row with
-- NULL pick_kind = the variant doesn't stamp (yet). songs_like_artist
-- is non-singleton (up to 3 mixes/user); a track in two of them takes
-- whichever LIMIT 1 hits — acceptable, tier stamps there describe the
-- same eligibility ladder.
SELECT pt.pick_kind
FROM playlist_tracks pt
JOIN playlists p ON p.id = pt.playlist_id
WHERE p.user_id = $1
  AND p.system_variant = $2
  AND pt.track_id = $3
LIMIT 1;

-- name: UpdatePlayEventEnded :one
-- Closes a play_event by id with the given ended_at, duration, and skip flag.
-- completion_ratio is computed from duration_played_ms and the track duration
-- (looked up by the caller — sqlc doesn't do joins on UPDATE).
UPDATE play_events
SET ended_at = $2,
    duration_played_ms = $3,
    completion_ratio = $4,
    was_skipped = $5
WHERE id = $1
RETURNING *;

-- name: GetPlayEventByID :one
SELECT * FROM play_events WHERE id = $1;

-- name: InsertSkipEvent :one
INSERT INTO skip_events (user_id, track_id, session_id, skipped_at, position_ms)
VALUES ($1, $2, $3, $4, $5)
RETURNING *;

-- name: ListRecentSessionTracks :many
-- Returns up to $3 tracks in session $1 whose play_event started before
-- $2, ordered newest-first. Used by playevents.RecordPlayStarted to
-- build the session_vector for the just-inserted play_event.
SELECT t.* FROM tracks t
JOIN play_events pe ON pe.track_id = t.id
WHERE pe.session_id = $1
  AND pe.started_at < $2
ORDER BY pe.started_at DESC
LIMIT $3;

-- name: UpdatePlayEventVector :exec
-- Used right after InsertPlayEvent to populate session_vector_at_play
-- once the vector has been computed.
UPDATE play_events
SET session_vector_at_play = $2
WHERE id = $1;

-- name: GetCurrentSessionVectorForUser :one
-- Returns the session_vector_at_play of the user's most recent play_event
-- in a still-active (un-timed-out) session. NoRows means no current vector.
-- Joined with play_sessions so closed sessions don't leak stale vectors.
SELECT pe.session_vector_at_play
FROM play_events pe
JOIN play_sessions s ON s.id = pe.session_id
WHERE pe.user_id = $1
  AND s.ended_at IS NULL
ORDER BY pe.started_at DESC
LIMIT 1;
