-- 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 source='for_you' plays whose track was
-- found in the user's live For You snapshot at ingestion time (#1249).
INSERT INTO play_events (
    user_id, track_id, session_id, started_at, client_id, source, pick_kind
) VALUES ($1, $2, $3, $4, $5, $6, sqlc.narg(pick_kind)::text)
RETURNING *;

-- name: GetForYouPickKindForTrack :one
-- Looks a track up in the user's CURRENT For You snapshot and returns
-- its pick_kind ('taste'/'fresh'). Used at play-ingestion time to
-- freeze exploration attribution onto the play_event — the snapshot is
-- rebuilt daily, so attribution can't be reconstructed at read time
-- (#1249). No row = track not in today's snapshot (caller stores NULL).
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 = 'for_you'
  AND pt.track_id = $2
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;
