-- name: UpsertTrack :one
-- file_path is the canonical identity for library scan; mbid is secondary.
INSERT INTO tracks (
    title, album_id, artist_id, track_number, disc_number,
    duration_ms, file_path, file_size, file_format, bitrate, mbid, genre
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT (file_path) DO UPDATE SET
    title = EXCLUDED.title,
    album_id = EXCLUDED.album_id,
    artist_id = EXCLUDED.artist_id,
    track_number = EXCLUDED.track_number,
    disc_number = EXCLUDED.disc_number,
    duration_ms = EXCLUDED.duration_ms,
    file_size = EXCLUDED.file_size,
    file_format = EXCLUDED.file_format,
    bitrate = EXCLUDED.bitrate,
    mbid = EXCLUDED.mbid,
    genre = EXCLUDED.genre,
    updated_at = now()
RETURNING *;

-- name: ListTracksMissingMbidWithPath :many
-- Track recording-MBID backfill: tracks with NULL mbid that still have
-- a file to re-read. $1 caps the batch (mirrors the album backfill).
SELECT id, file_path
  FROM tracks
 WHERE mbid IS NULL
 ORDER BY id
 LIMIT $1;

-- name: SetTrackMbidIfNull :exec
-- Heal a track's recording MBID only while still NULL — idempotent, so
-- re-running the backfill is a no-op for already-healed rows.
UPDATE tracks
   SET mbid = $2, updated_at = now()
 WHERE id = $1 AND mbid IS NULL;

-- name: GetTrackByID :one
SELECT * FROM tracks WHERE id = $1;

-- name: GetTrackByPath :one
SELECT * FROM tracks WHERE file_path = $1;

-- name: ListTracksByAlbum :many
-- $1 = album_id, $2 = user_id. Pass pgtype.UUID{Valid: false} (NULL)
-- to skip the per-user quarantine filter; the NOT EXISTS clause on
-- a NULL user_id never matches a row, so every track passes through.
SELECT * FROM tracks
WHERE album_id = $1
  AND NOT EXISTS (
    SELECT 1 FROM lidarr_quarantine q
    WHERE q.user_id = $2 AND q.track_id = tracks.id
  )
ORDER BY disc_number NULLS LAST, track_number NULLS LAST;

-- name: CountTracksByAlbum :one
SELECT count(*) FROM tracks WHERE album_id = $1;

-- name: SearchTracks :many
-- $1 = title query, $2 = user_id (NULL to skip quarantine filter),
-- $3 = limit, $4 = offset.
SELECT * FROM tracks
WHERE title ILIKE '%' || $1::text || '%'
  AND NOT EXISTS (
    SELECT 1 FROM lidarr_quarantine q
    WHERE q.user_id = $2 AND q.track_id = tracks.id
  )
ORDER BY title
LIMIT $3 OFFSET $4;

-- name: CountTracksMatching :one
-- $1 = title query, $2 = user_id (NULL to skip quarantine filter).
SELECT COUNT(*) FROM tracks
WHERE title ILIKE '%' || $1::text || '%'
  AND NOT EXISTS (
    SELECT 1 FROM lidarr_quarantine q
    WHERE q.user_id = $2 AND q.track_id = tracks.id
  );

-- name: ListArtistTracksForUser :many
-- M6a: every track for the artist across their albums, with album_title
-- and artist_name joined. Honors per-user lidarr_quarantine. Used by
-- /api/artists/{id}/tracks for the artist-card play affordance, which
-- shuffles client-side. Ordering matches album/track natural order so
-- the shuffle has a deterministic input.
SELECT sqlc.embed(t),
       albums.title AS album_title,
       artists.name AS artist_name
FROM tracks t
JOIN albums  ON albums.id  = t.album_id
JOIN artists ON artists.id = t.artist_id
WHERE t.artist_id = $1
  AND NOT EXISTS (
      SELECT 1 FROM lidarr_quarantine q
      WHERE q.user_id = $2 AND q.track_id = t.id
  )
ORDER BY albums.release_date NULLS LAST, albums.sort_title,
         t.disc_number NULLS FIRST, t.track_number NULLS FIRST, t.id;

-- name: ListRandomTracksForUser :many
-- #427 S4: backing query for GET /api/library/shuffle — the online
-- source for "Shuffle all". N random tracks across the whole
-- library, per-user-quarantine filtered. $1 user_id, $2 limit.
SELECT sqlc.embed(t),
       albums.title AS album_title,
       artists.name AS artist_name
FROM tracks t
JOIN albums  ON albums.id  = t.album_id
JOIN artists ON artists.id = t.artist_id
WHERE NOT EXISTS (
      SELECT 1 FROM lidarr_quarantine q
      WHERE q.user_id = $1 AND q.track_id = t.id
  )
ORDER BY random()
LIMIT $2;

-- name: CountTracksByArtist :one
-- Used by request-progress reporting to count tracks ingested under a
-- matched artist (sum across all the artist's albums) while a request
-- is still in flight.
SELECT COUNT(*) FROM tracks WHERE artist_id = $1;

-- name: DeleteTrack :one
-- M7 #372: hard delete with FK cascade. The CASCADE on track_id from
-- play_events / general_likes_tracks / lidarr_quarantine /
-- lidarr_quarantine_actions handles their cleanup. RETURNING gives us
-- album_id + artist_id for the album-empty / artist-empty cascade
-- checks the service does next.
DELETE FROM tracks WHERE id = $1
RETURNING id, album_id, artist_id, file_path, mbid;

-- name: GetTracksByIDs :many
-- Batched lookup used by /api/library/sync to hydrate upsert payloads
-- (#357). Mirror of GetArtistsByIDs.
SELECT * FROM tracks WHERE id = ANY($1::uuid[]);
