// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.30.0
// source: audio_files.sql

package sqlcgen

import (
	"context"
	"database/sql"
	"strings"
)

const countAudioFiles = `-- name: CountAudioFiles :one
SELECT COUNT(*) AS count FROM audio_files
WHERE library_id = COALESCE(NULLIF(CAST(?1 AS INTEGER), 0), library_id)
`

func (q *Queries) CountAudioFiles(ctx context.Context, libraryID int64) (int64, error) {
	row := q.db.QueryRowContext(ctx, countAudioFiles, libraryID)
	var count int64
	err := row.Scan(&count)
	return count, err
}

const createAudioFile = `-- name: CreateAudioFile :one


INSERT INTO audio_files (
  file_path, library_id, file_type_id,
  length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size,
  title, artist_credit, artist_id, album_id,
  track_number, disc_number, total_tracks, year, composer, comment,
  recording_mbid, basename, group_key, modified_at, tag_status
) VALUES (
  ?, ?, ?,
  ?, ?, ?, ?, ?, ?,
  ?, ?, ?, ?,
  ?, ?, ?, ?, ?, ?,
  ?, ?, ?, ?, ?
)
RETURNING id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, tag_status
`

type CreateAudioFileParams struct {
	FilePath           string
	LibraryID          int64
	FileTypeID         int64
	LengthMilliseconds int64
	SampleRate         int64
	BitDepth           int64
	Channels           int64
	Bitrate            int64
	FileSize           int64
	Title              string
	ArtistCredit       string
	ArtistID           sql.NullInt64
	AlbumID            sql.NullInt64
	TrackNumber        sql.NullInt64
	DiscNumber         sql.NullInt64
	TotalTracks        sql.NullInt64
	Year               sql.NullInt64
	Composer           string
	Comment            string
	RecordingMbid      sql.NullString
	Basename           string
	GroupKey           string
	ModifiedAt         int64
	TagStatus          string
}

// Queries over audio_files and the track_metadata view above it.
//
// Every query that returns "a track" selects from `track_metadata`,
// which is the one place the projection is defined.  The scoped and
// unscoped variants that used to be written twice are one query now:
// library_id 0 means "every library", and `(:id = 0 OR library_id = :id)`
// costs nothing measurable (23 ms vs 21 ms over 26k rows) because these
// queries scan either way.
// ---------------------------------------------------------------------
// Writes
// ---------------------------------------------------------------------
func (q *Queries) CreateAudioFile(ctx context.Context, arg CreateAudioFileParams) (AudioFile, error) {
	row := q.db.QueryRowContext(ctx, createAudioFile,
		arg.FilePath,
		arg.LibraryID,
		arg.FileTypeID,
		arg.LengthMilliseconds,
		arg.SampleRate,
		arg.BitDepth,
		arg.Channels,
		arg.Bitrate,
		arg.FileSize,
		arg.Title,
		arg.ArtistCredit,
		arg.ArtistID,
		arg.AlbumID,
		arg.TrackNumber,
		arg.DiscNumber,
		arg.TotalTracks,
		arg.Year,
		arg.Composer,
		arg.Comment,
		arg.RecordingMbid,
		arg.Basename,
		arg.GroupKey,
		arg.ModifiedAt,
		arg.TagStatus,
	)
	var i AudioFile
	err := row.Scan(
		&i.ID,
		&i.FilePath,
		&i.LibraryID,
		&i.FileTypeID,
		&i.LengthMilliseconds,
		&i.SampleRate,
		&i.BitDepth,
		&i.Channels,
		&i.Bitrate,
		&i.FileSize,
		&i.Title,
		&i.ArtistCredit,
		&i.ArtistID,
		&i.AlbumID,
		&i.TrackNumber,
		&i.DiscNumber,
		&i.TotalTracks,
		&i.Year,
		&i.Composer,
		&i.Comment,
		&i.RecordingMbid,
		&i.Basename,
		&i.GroupKey,
		&i.ModifiedAt,
		&i.PlayCount,
		&i.LastPlayed,
		&i.TagStatus,
	)
	return i, err
}

const deleteAllAudioFiles = `-- name: DeleteAllAudioFiles :exec
DELETE FROM audio_files
`

func (q *Queries) DeleteAllAudioFiles(ctx context.Context) error {
	_, err := q.db.ExecContext(ctx, deleteAllAudioFiles)
	return err
}

const deleteAudioFile = `-- name: DeleteAudioFile :exec
DELETE FROM audio_files WHERE id = ?
`

func (q *Queries) DeleteAudioFile(ctx context.Context, id int64) error {
	_, err := q.db.ExecContext(ctx, deleteAudioFile, id)
	return err
}

const getAllAudioFilePaths = `-- name: GetAllAudioFilePaths :many
SELECT id, file_path FROM audio_files
`

type GetAllAudioFilePathsRow struct {
	ID       int64
	FilePath string
}

func (q *Queries) GetAllAudioFilePaths(ctx context.Context) ([]GetAllAudioFilePathsRow, error) {
	rows, err := q.db.QueryContext(ctx, getAllAudioFilePaths)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetAllAudioFilePathsRow
	for rows.Next() {
		var i GetAllAudioFilePathsRow
		if err := rows.Scan(&i.ID, &i.FilePath); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getAudioFile = `-- name: GetAudioFile :one

SELECT id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, tag_status FROM audio_files WHERE id = ? LIMIT 1
`

// ---------------------------------------------------------------------
// Reads: the file row itself
// ---------------------------------------------------------------------
func (q *Queries) GetAudioFile(ctx context.Context, id int64) (AudioFile, error) {
	row := q.db.QueryRowContext(ctx, getAudioFile, id)
	var i AudioFile
	err := row.Scan(
		&i.ID,
		&i.FilePath,
		&i.LibraryID,
		&i.FileTypeID,
		&i.LengthMilliseconds,
		&i.SampleRate,
		&i.BitDepth,
		&i.Channels,
		&i.Bitrate,
		&i.FileSize,
		&i.Title,
		&i.ArtistCredit,
		&i.ArtistID,
		&i.AlbumID,
		&i.TrackNumber,
		&i.DiscNumber,
		&i.TotalTracks,
		&i.Year,
		&i.Composer,
		&i.Comment,
		&i.RecordingMbid,
		&i.Basename,
		&i.GroupKey,
		&i.ModifiedAt,
		&i.PlayCount,
		&i.LastPlayed,
		&i.TagStatus,
	)
	return i, err
}

const getAudioFileByPath = `-- name: GetAudioFileByPath :one
SELECT id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, tag_status FROM audio_files WHERE file_path = ? LIMIT 1
`

func (q *Queries) GetAudioFileByPath(ctx context.Context, filePath string) (AudioFile, error) {
	row := q.db.QueryRowContext(ctx, getAudioFileByPath, filePath)
	var i AudioFile
	err := row.Scan(
		&i.ID,
		&i.FilePath,
		&i.LibraryID,
		&i.FileTypeID,
		&i.LengthMilliseconds,
		&i.SampleRate,
		&i.BitDepth,
		&i.Channels,
		&i.Bitrate,
		&i.FileSize,
		&i.Title,
		&i.ArtistCredit,
		&i.ArtistID,
		&i.AlbumID,
		&i.TrackNumber,
		&i.DiscNumber,
		&i.TotalTracks,
		&i.Year,
		&i.Composer,
		&i.Comment,
		&i.RecordingMbid,
		&i.Basename,
		&i.GroupKey,
		&i.ModifiedAt,
		&i.PlayCount,
		&i.LastPlayed,
		&i.TagStatus,
	)
	return i, err
}

const getAudioFileGroupKey = `-- name: GetAudioFileGroupKey :one
SELECT group_key FROM audio_files WHERE id = ? LIMIT 1
`

func (q *Queries) GetAudioFileGroupKey(ctx context.Context, id int64) (string, error) {
	row := q.db.QueryRowContext(ctx, getAudioFileGroupKey, id)
	var group_key string
	err := row.Scan(&group_key)
	return group_key, err
}

const getAudioFilesByPaths = `-- name: GetAudioFilesByPaths :many
SELECT id, library_id, file_path, group_key FROM audio_files
WHERE file_path IN (/*SLICE:paths*/?)
`

type GetAudioFilesByPathsRow struct {
	ID        int64
	LibraryID int64
	FilePath  string
	GroupKey  string
}

func (q *Queries) GetAudioFilesByPaths(ctx context.Context, paths []string) ([]GetAudioFilesByPathsRow, error) {
	query := getAudioFilesByPaths
	var queryParams []interface{}
	if len(paths) > 0 {
		for _, v := range paths {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:paths*/?", strings.Repeat(",?", len(paths))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:paths*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetAudioFilesByPathsRow
	for rows.Next() {
		var i GetAudioFilesByPathsRow
		if err := rows.Scan(
			&i.ID,
			&i.LibraryID,
			&i.FilePath,
			&i.GroupKey,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getAudioFilesInLibrary = `-- name: GetAudioFilesInLibrary :many
SELECT id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, tag_status FROM audio_files WHERE library_id = ?
`

func (q *Queries) GetAudioFilesInLibrary(ctx context.Context, libraryID int64) ([]AudioFile, error) {
	rows, err := q.db.QueryContext(ctx, getAudioFilesInLibrary, libraryID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []AudioFile
	for rows.Next() {
		var i AudioFile
		if err := rows.Scan(
			&i.ID,
			&i.FilePath,
			&i.LibraryID,
			&i.FileTypeID,
			&i.LengthMilliseconds,
			&i.SampleRate,
			&i.BitDepth,
			&i.Channels,
			&i.Bitrate,
			&i.FileSize,
			&i.Title,
			&i.ArtistCredit,
			&i.ArtistID,
			&i.AlbumID,
			&i.TrackNumber,
			&i.DiscNumber,
			&i.TotalTracks,
			&i.Year,
			&i.Composer,
			&i.Comment,
			&i.RecordingMbid,
			&i.Basename,
			&i.GroupKey,
			&i.ModifiedAt,
			&i.PlayCount,
			&i.LastPlayed,
			&i.TagStatus,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getFilePathsByAlbums = `-- name: GetFilePathsByAlbums :many

SELECT album_id, library_id, file_path FROM audio_files
WHERE album_id IN (/*SLICE:album_ids*/?)
ORDER BY disc_number, track_number
`

type GetFilePathsByAlbumsRow struct {
	AlbumID   sql.NullInt64
	LibraryID int64
	FilePath  string
}

// ---------------------------------------------------------------------
// Reads: file paths, grouped by whatever the caller asked about
// ---------------------------------------------------------------------
// These answer "what can I play" and they all ask audio_files, because
// that is the only table whose rows are files.  Grouped rather than
// flattened because the caller owns the order.
// The library filter is applied in Go rather than here: sqlc numbers a
// named parameter (?2) but expands a slice into N placeholders, so the
// two together bind the wrong values - GetFilePathsByAlbums([1,2], 0)
// read album id 2 as the library id.  Returning library_id and
// filtering the (small) result is the version that cannot be wrong.
func (q *Queries) GetFilePathsByAlbums(ctx context.Context, albumIds []sql.NullInt64) ([]GetFilePathsByAlbumsRow, error) {
	query := getFilePathsByAlbums
	var queryParams []interface{}
	if len(albumIds) > 0 {
		for _, v := range albumIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:album_ids*/?", strings.Repeat(",?", len(albumIds))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:album_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetFilePathsByAlbumsRow
	for rows.Next() {
		var i GetFilePathsByAlbumsRow
		if err := rows.Scan(&i.AlbumID, &i.LibraryID, &i.FilePath); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getFilePathsByArtistMBID = `-- name: GetFilePathsByArtistMBID :many
SELECT DISTINCT af.file_path
FROM audio_files af
JOIN artists a ON a.id = af.artist_id
WHERE a.mbid = ?
`

func (q *Queries) GetFilePathsByArtistMBID(ctx context.Context, mbid sql.NullString) ([]string, error) {
	rows, err := q.db.QueryContext(ctx, getFilePathsByArtistMBID, mbid)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []string
	for rows.Next() {
		var file_path string
		if err := rows.Scan(&file_path); err != nil {
			return nil, err
		}
		items = append(items, file_path)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getFilePathsByGenres = `-- name: GetFilePathsByGenres :many
SELECT g.name AS genre, af.library_id, af.file_path
FROM audio_files af
JOIN file_genres fg ON fg.audio_file_id = af.id
JOIN genres g ON g.id = fg.genre_id
WHERE g.name IN (/*SLICE:genres*/?)
ORDER BY af.disc_number, af.track_number
`

type GetFilePathsByGenresRow struct {
	Genre     string
	LibraryID int64
	FilePath  string
}

func (q *Queries) GetFilePathsByGenres(ctx context.Context, genres []string) ([]GetFilePathsByGenresRow, error) {
	query := getFilePathsByGenres
	var queryParams []interface{}
	if len(genres) > 0 {
		for _, v := range genres {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:genres*/?", strings.Repeat(",?", len(genres))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:genres*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetFilePathsByGenresRow
	for rows.Next() {
		var i GetFilePathsByGenresRow
		if err := rows.Scan(&i.Genre, &i.LibraryID, &i.FilePath); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getFilePathsByRecordingMBIDs = `-- name: GetFilePathsByRecordingMBIDs :many
SELECT recording_mbid, library_id, file_path FROM audio_files
WHERE recording_mbid IN (/*SLICE:mbids*/?)
ORDER BY file_path
`

type GetFilePathsByRecordingMBIDsRow struct {
	RecordingMbid sql.NullString
	LibraryID     int64
	FilePath      string
}

// The ownership question in its only honest form: which of these
// catalog recordings has a *file* behind it.  Asked of audio_files, so
// a metadata row with no file cannot answer yes.
func (q *Queries) GetFilePathsByRecordingMBIDs(ctx context.Context, mbids []sql.NullString) ([]GetFilePathsByRecordingMBIDsRow, error) {
	query := getFilePathsByRecordingMBIDs
	var queryParams []interface{}
	if len(mbids) > 0 {
		for _, v := range mbids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:mbids*/?", strings.Repeat(",?", len(mbids))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:mbids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetFilePathsByRecordingMBIDsRow
	for rows.Next() {
		var i GetFilePathsByRecordingMBIDsRow
		if err := rows.Scan(&i.RecordingMbid, &i.LibraryID, &i.FilePath); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getLibraryMaxModifiedAt = `-- name: GetLibraryMaxModifiedAt :one
SELECT CAST(COALESCE(MAX(modified_at), 0) AS INTEGER) FROM audio_files
WHERE library_id = ?
`

// Newest recorded mtime in a library, for the startup soft scan.  0 when
// the library is empty or no row has a baseline yet.
func (q *Queries) GetLibraryMaxModifiedAt(ctx context.Context, libraryID int64) (int64, error) {
	row := q.db.QueryRowContext(ctx, getLibraryMaxModifiedAt, libraryID)
	var column_1 int64
	err := row.Scan(&column_1)
	return column_1, err
}

const getRandomAudioFilePath = `-- name: GetRandomAudioFilePath :one
SELECT file_path FROM audio_files ORDER BY RANDOM() LIMIT 1
`

func (q *Queries) GetRandomAudioFilePath(ctx context.Context) (string, error) {
	row := q.db.QueryRowContext(ctx, getRandomAudioFilePath)
	var file_path string
	err := row.Scan(&file_path)
	return file_path, err
}

const getTrackByPath = `-- name: GetTrackByPath :one
SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata WHERE file_path = ? LIMIT 1
`

func (q *Queries) GetTrackByPath(ctx context.Context, filePath string) (TrackMetadatum, error) {
	row := q.db.QueryRowContext(ctx, getTrackByPath, filePath)
	var i TrackMetadatum
	err := row.Scan(
		&i.ID,
		&i.FilePath,
		&i.LengthMilliseconds,
		&i.Title,
		&i.ArtistName,
		&i.TrackNumber,
		&i.DiscNumber,
		&i.Album,
		&i.Genre,
		&i.Year,
		&i.ReleaseYear,
		&i.Composer,
		&i.FileType,
		&i.SampleRate,
		&i.BitDepth,
		&i.Channels,
		&i.Bitrate,
		&i.FileSize,
		&i.LibraryID,
		&i.PlayCount,
		&i.LastPlayed,
		&i.CoverArtPath,
		&i.ArtistMbid,
		&i.ReleaseGroupMbid,
		&i.RecordingMbid,
		&i.AlbumID,
		&i.ArtistID,
	)
	return i, err
}

const getTracks = `-- name: GetTracks :many

SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata
WHERE library_id = COALESCE(NULLIF(CAST(?1 AS INTEGER), 0), library_id)
`

// ---------------------------------------------------------------------
// Reads: tracks
// ---------------------------------------------------------------------
func (q *Queries) GetTracks(ctx context.Context, libraryID int64) ([]TrackMetadatum, error) {
	rows, err := q.db.QueryContext(ctx, getTracks, libraryID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []TrackMetadatum
	for rows.Next() {
		var i TrackMetadatum
		if err := rows.Scan(
			&i.ID,
			&i.FilePath,
			&i.LengthMilliseconds,
			&i.Title,
			&i.ArtistName,
			&i.TrackNumber,
			&i.DiscNumber,
			&i.Album,
			&i.Genre,
			&i.Year,
			&i.ReleaseYear,
			&i.Composer,
			&i.FileType,
			&i.SampleRate,
			&i.BitDepth,
			&i.Channels,
			&i.Bitrate,
			&i.FileSize,
			&i.LibraryID,
			&i.PlayCount,
			&i.LastPlayed,
			&i.CoverArtPath,
			&i.ArtistMbid,
			&i.ReleaseGroupMbid,
			&i.RecordingMbid,
			&i.AlbumID,
			&i.ArtistID,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getTracksByAlbum = `-- name: GetTracksByAlbum :many
SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata
WHERE album_id = ?1
  AND library_id = COALESCE(NULLIF(CAST(?2 AS INTEGER), 0), library_id)
ORDER BY disc_number, track_number
`

type GetTracksByAlbumParams struct {
	AlbumID   sql.NullInt64
	LibraryID int64
}

func (q *Queries) GetTracksByAlbum(ctx context.Context, arg GetTracksByAlbumParams) ([]TrackMetadatum, error) {
	rows, err := q.db.QueryContext(ctx, getTracksByAlbum, arg.AlbumID, arg.LibraryID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []TrackMetadatum
	for rows.Next() {
		var i TrackMetadatum
		if err := rows.Scan(
			&i.ID,
			&i.FilePath,
			&i.LengthMilliseconds,
			&i.Title,
			&i.ArtistName,
			&i.TrackNumber,
			&i.DiscNumber,
			&i.Album,
			&i.Genre,
			&i.Year,
			&i.ReleaseYear,
			&i.Composer,
			&i.FileType,
			&i.SampleRate,
			&i.BitDepth,
			&i.Channels,
			&i.Bitrate,
			&i.FileSize,
			&i.LibraryID,
			&i.PlayCount,
			&i.LastPlayed,
			&i.CoverArtPath,
			&i.ArtistMbid,
			&i.ReleaseGroupMbid,
			&i.RecordingMbid,
			&i.AlbumID,
			&i.ArtistID,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getTracksByGenre = `-- name: GetTracksByGenre :many
SELECT tm.id, tm.file_path, tm.length_milliseconds, tm.title, tm.artist_name, tm.track_number, tm.disc_number, tm.album, tm.genre, tm.year, tm.release_year, tm.composer, tm.file_type, tm.sample_rate, tm.bit_depth, tm.channels, tm.bitrate, tm.file_size, tm.library_id, tm.play_count, tm.last_played, tm.cover_art_path, tm.artist_mbid, tm.release_group_mbid, tm.recording_mbid, tm.album_id, tm.artist_id FROM track_metadata tm
JOIN file_genres fg ON fg.audio_file_id = tm.id
JOIN genres g ON g.id = fg.genre_id
WHERE g.name = ?1
  AND tm.library_id = COALESCE(NULLIF(CAST(?2 AS INTEGER), 0), tm.library_id)
`

type GetTracksByGenreParams struct {
	Genre     string
	LibraryID int64
}

func (q *Queries) GetTracksByGenre(ctx context.Context, arg GetTracksByGenreParams) ([]TrackMetadatum, error) {
	rows, err := q.db.QueryContext(ctx, getTracksByGenre, arg.Genre, arg.LibraryID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []TrackMetadatum
	for rows.Next() {
		var i TrackMetadatum
		if err := rows.Scan(
			&i.ID,
			&i.FilePath,
			&i.LengthMilliseconds,
			&i.Title,
			&i.ArtistName,
			&i.TrackNumber,
			&i.DiscNumber,
			&i.Album,
			&i.Genre,
			&i.Year,
			&i.ReleaseYear,
			&i.Composer,
			&i.FileType,
			&i.SampleRate,
			&i.BitDepth,
			&i.Channels,
			&i.Bitrate,
			&i.FileSize,
			&i.LibraryID,
			&i.PlayCount,
			&i.LastPlayed,
			&i.CoverArtPath,
			&i.ArtistMbid,
			&i.ReleaseGroupMbid,
			&i.RecordingMbid,
			&i.AlbumID,
			&i.ArtistID,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const lookupTrackMetaByPaths = `-- name: LookupTrackMetaByPaths :many
SELECT id, file_path, title, artist_name, album, cover_art_path,
       artist_mbid, release_group_mbid, recording_mbid
FROM track_metadata
WHERE file_path IN (/*SLICE:paths*/?)
`

type LookupTrackMetaByPathsRow struct {
	ID               int64
	FilePath         string
	Title            string
	ArtistName       string
	Album            string
	CoverArtPath     string
	ArtistMbid       string
	ReleaseGroupMbid string
	RecordingMbid    string
}

func (q *Queries) LookupTrackMetaByPaths(ctx context.Context, paths []string) ([]LookupTrackMetaByPathsRow, error) {
	query := lookupTrackMetaByPaths
	var queryParams []interface{}
	if len(paths) > 0 {
		for _, v := range paths {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:paths*/?", strings.Repeat(",?", len(paths))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:paths*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []LookupTrackMetaByPathsRow
	for rows.Next() {
		var i LookupTrackMetaByPathsRow
		if err := rows.Scan(
			&i.ID,
			&i.FilePath,
			&i.Title,
			&i.ArtistName,
			&i.Album,
			&i.CoverArtPath,
			&i.ArtistMbid,
			&i.ReleaseGroupMbid,
			&i.RecordingMbid,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const ownedAlbumMBIDs = `-- name: OwnedAlbumMBIDs :many
SELECT DISTINCT al.mbid FROM albums al
JOIN audio_files af ON af.album_id = al.id
WHERE al.mbid IN (/*SLICE:mbids*/?)
`

func (q *Queries) OwnedAlbumMBIDs(ctx context.Context, mbids []sql.NullString) ([]sql.NullString, error) {
	query := ownedAlbumMBIDs
	var queryParams []interface{}
	if len(mbids) > 0 {
		for _, v := range mbids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:mbids*/?", strings.Repeat(",?", len(mbids))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:mbids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []sql.NullString
	for rows.Next() {
		var mbid sql.NullString
		if err := rows.Scan(&mbid); err != nil {
			return nil, err
		}
		items = append(items, mbid)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const ownedArtistMBIDs = `-- name: OwnedArtistMBIDs :many
SELECT DISTINCT a.mbid FROM artists a
JOIN audio_files af ON af.artist_id = a.id
WHERE a.mbid IN (/*SLICE:mbids*/?)
`

func (q *Queries) OwnedArtistMBIDs(ctx context.Context, mbids []sql.NullString) ([]sql.NullString, error) {
	query := ownedArtistMBIDs
	var queryParams []interface{}
	if len(mbids) > 0 {
		for _, v := range mbids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:mbids*/?", strings.Repeat(",?", len(mbids))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:mbids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []sql.NullString
	for rows.Next() {
		var mbid sql.NullString
		if err := rows.Scan(&mbid); err != nil {
			return nil, err
		}
		items = append(items, mbid)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const ownedRecordingMBIDs = `-- name: OwnedRecordingMBIDs :many

SELECT DISTINCT recording_mbid FROM audio_files
WHERE recording_mbid IN (/*SLICE:mbids*/?)
`

// ---------------------------------------------------------------------
// Ownership, asked in bulk
// ---------------------------------------------------------------------
// Which of these recording MBIDs are actually in the library.  This is
// what marks a catalog tracklist owned; it used to be
// `SELECT mbid FROM recordings`, which answered yes for 129 tracks in a
// real library that had no file at all.
func (q *Queries) OwnedRecordingMBIDs(ctx context.Context, mbids []sql.NullString) ([]sql.NullString, error) {
	query := ownedRecordingMBIDs
	var queryParams []interface{}
	if len(mbids) > 0 {
		for _, v := range mbids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:mbids*/?", strings.Repeat(",?", len(mbids))[1:], 1)
	} else {
		query = strings.Replace(query, "/*SLICE:mbids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []sql.NullString
	for rows.Next() {
		var recording_mbid sql.NullString
		if err := rows.Scan(&recording_mbid); err != nil {
			return nil, err
		}
		items = append(items, recording_mbid)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const promoteAudioFileTagStatusIfUntagged = `-- name: PromoteAudioFileTagStatusIfUntagged :exec
UPDATE audio_files
SET tag_status = 'user_confirmed'
WHERE id = ? AND tag_status = 'untagged'
`

// A rescan re-reads the tags of a file whose mtime moved, so a file
// another tagger stamped with MBIDs since import arrives here still
// carrying the 'untagged' status it was created with (only the insert
// path sets it).  Promote it the same way saveAudioFile does.
// Guarded on 'untagged' so it cannot overwrite a deliberate
// 'user_skipped_permanent', and so a file losing its MBIDs is left
// alone -- demotion is the scan's judgement, not this statement's.
func (q *Queries) PromoteAudioFileTagStatusIfUntagged(ctx context.Context, id int64) error {
	_, err := q.db.ExecContext(ctx, promoteAudioFileTagStatusIfUntagged, id)
	return err
}

const searchTracksByBasename = `-- name: SearchTracksByBasename :many
SELECT id, file_path, length_milliseconds, title, artist_name, album
FROM track_metadata
WHERE file_path IN (
    SELECT file_path FROM audio_files WHERE basename = ?1
)
LIMIT ?2
`

type SearchTracksByBasenameParams struct {
	Basename string
	Lim      int64
}

type SearchTracksByBasenameRow struct {
	ID                 int64
	FilePath           string
	LengthMilliseconds int64
	Title              string
	ArtistName         string
	Album              string
}

func (q *Queries) SearchTracksByBasename(ctx context.Context, arg SearchTracksByBasenameParams) ([]SearchTracksByBasenameRow, error) {
	rows, err := q.db.QueryContext(ctx, searchTracksByBasename, arg.Basename, arg.Lim)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []SearchTracksByBasenameRow
	for rows.Next() {
		var i SearchTracksByBasenameRow
		if err := rows.Scan(
			&i.ID,
			&i.FilePath,
			&i.LengthMilliseconds,
			&i.Title,
			&i.ArtistName,
			&i.Album,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const setAudioFileGroupKey = `-- name: SetAudioFileGroupKey :exec
UPDATE audio_files SET group_key = ? WHERE id = ?
`

type SetAudioFileGroupKeyParams struct {
	GroupKey string
	ID       int64
}

func (q *Queries) SetAudioFileGroupKey(ctx context.Context, arg SetAudioFileGroupKeyParams) error {
	_, err := q.db.ExecContext(ctx, setAudioFileGroupKey, arg.GroupKey, arg.ID)
	return err
}

const setAudioFileRecordingMBID = `-- name: SetAudioFileRecordingMBID :exec
UPDATE audio_files SET recording_mbid = ? WHERE id = ?
`

type SetAudioFileRecordingMBIDParams struct {
	RecordingMbid sql.NullString
	ID            int64
}

func (q *Queries) SetAudioFileRecordingMBID(ctx context.Context, arg SetAudioFileRecordingMBIDParams) error {
	_, err := q.db.ExecContext(ctx, setAudioFileRecordingMBID, arg.RecordingMbid, arg.ID)
	return err
}

const updateAudioFileStat = `-- name: UpdateAudioFileStat :exec
UPDATE audio_files
SET modified_at = ?, file_size = ?
WHERE id = ?
`

type UpdateAudioFileStatParams struct {
	ModifiedAt int64
	FileSize   int64
	ID         int64
}

// Records the on-disk mtime/size without re-reading tags.  Used to
// backfill the staleness baseline for files the scan skipped, and to
// re-baseline after YellowJacket's own tag writer rewrites a file.
func (q *Queries) UpdateAudioFileStat(ctx context.Context, arg UpdateAudioFileStatParams) error {
	_, err := q.db.ExecContext(ctx, updateAudioFileStat, arg.ModifiedAt, arg.FileSize, arg.ID)
	return err
}

const updateAudioFileTags = `-- name: UpdateAudioFileTags :exec
UPDATE audio_files
SET title = ?, artist_credit = ?, artist_id = ?, album_id = ?,
    track_number = ?, disc_number = ?, total_tracks = ?, year = ?,
    composer = ?, comment = ?, recording_mbid = ?,
    sample_rate = ?, bit_depth = ?, channels = ?, bitrate = ?,
    file_size = ?, length_milliseconds = ?, modified_at = ?
WHERE id = ?
`

type UpdateAudioFileTagsParams struct {
	Title              string
	ArtistCredit       string
	ArtistID           sql.NullInt64
	AlbumID            sql.NullInt64
	TrackNumber        sql.NullInt64
	DiscNumber         sql.NullInt64
	TotalTracks        sql.NullInt64
	Year               sql.NullInt64
	Composer           string
	Comment            string
	RecordingMbid      sql.NullString
	SampleRate         int64
	BitDepth           int64
	Channels           int64
	Bitrate            int64
	FileSize           int64
	LengthMilliseconds int64
	ModifiedAt         int64
	ID                 int64
}

// A rescan of a file whose mtime moved: the tags are re-read and
// written over the same row.  Under the old schema this created a
// *new* recording and repointed the file at it, abandoning the old one
// -- which is where 812 orphaned rows and every phantom "you own this"
// came from.  There is nothing to orphan now.
func (q *Queries) UpdateAudioFileTags(ctx context.Context, arg UpdateAudioFileTagsParams) error {
	_, err := q.db.ExecContext(ctx, updateAudioFileTags,
		arg.Title,
		arg.ArtistCredit,
		arg.ArtistID,
		arg.AlbumID,
		arg.TrackNumber,
		arg.DiscNumber,
		arg.TotalTracks,
		arg.Year,
		arg.Composer,
		arg.Comment,
		arg.RecordingMbid,
		arg.SampleRate,
		arg.BitDepth,
		arg.Channels,
		arg.Bitrate,
		arg.FileSize,
		arg.LengthMilliseconds,
		arg.ModifiedAt,
		arg.ID,
	)
	return err
}
