// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.31.1
// source: browse.sql

package dbq

import (
	"context"

	"github.com/jackc/pgx/v5/pgtype"
)

const countAlbumsByGenre = `-- name: CountAlbumsByGenre :one
SELECT COUNT(*) FROM albums
WHERE EXISTS (
    SELECT 1
    FROM tracks
    JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true
    WHERE tracks.album_id = albums.id
      AND tracks.missing_since IS NULL
      AND trim(g.genre) = trim($1::text)
)
`

// Total for the paging envelope. EXISTS mirrors the list query exactly; a
// JOIN + DISTINCT here would count differently the moment an album has two
// tracks carrying the same genre.
func (q *Queries) CountAlbumsByGenre(ctx context.Context, genre string) (int64, error) {
	row := q.db.QueryRow(ctx, countAlbumsByGenre, genre)
	var count int64
	err := row.Scan(&count)
	return count, err
}

const countAlbumsByYearRange = `-- name: CountAlbumsByYearRange :one
SELECT COUNT(*) FROM albums
WHERE release_date IS NOT NULL
  AND EXTRACT(YEAR FROM release_date)::int
      BETWEEN $1::int AND $2::int
`

type CountAlbumsByYearRangeParams struct {
	YearFrom int32
	YearTo   int32
}

func (q *Queries) CountAlbumsByYearRange(ctx context.Context, arg CountAlbumsByYearRangeParams) (int64, error) {
	row := q.db.QueryRow(ctx, countAlbumsByYearRange, arg.YearFrom, arg.YearTo)
	var count int64
	err := row.Scan(&count)
	return count, err
}

const listAlbumYearsWithCount = `-- name: ListAlbumYearsWithCount :many
SELECT EXTRACT(YEAR FROM release_date)::int AS year, COUNT(*)::bigint AS album_count
FROM albums
WHERE release_date IS NOT NULL
GROUP BY year
ORDER BY year DESC
`

type ListAlbumYearsWithCountRow struct {
	Year       int32
	AlbumCount int64
}

// Year browse index (#367). Only albums with a release_date appear — an
// album with no date isn't "year unknown" as a browsable bucket, it's absent
// from this axis, and the UI says so rather than inventing a 0 row.
// Newest first: recent releases are the likelier browse target.
func (q *Queries) ListAlbumYearsWithCount(ctx context.Context) ([]ListAlbumYearsWithCountRow, error) {
	rows, err := q.db.Query(ctx, listAlbumYearsWithCount)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListAlbumYearsWithCountRow
	for rows.Next() {
		var i ListAlbumYearsWithCountRow
		if err := rows.Scan(&i.Year, &i.AlbumCount); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listAlbumsByGenreWithArtist = `-- name: ListAlbumsByGenreWithArtist :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.name AS artist_name
FROM albums
JOIN artists ON artists.id = albums.artist_id
WHERE EXISTS (
    SELECT 1
    FROM tracks
    JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true
    WHERE tracks.album_id = albums.id
      AND tracks.missing_since IS NULL
      AND trim(g.genre) = trim($1::text)
)
ORDER BY albums.sort_title, albums.id
LIMIT $3 OFFSET $2
`

type ListAlbumsByGenreWithArtistParams struct {
	Genre string
	Off   int32
	Lim   int32
}

type ListAlbumsByGenreWithArtistRow struct {
	Album      Album
	ArtistName string
}

// Albums for one genre, joined with artist_name for the browse grid.
// An album belongs to a genre when ANY of its tracks carry it. Splits and
// trims identically to ListGenresWithCount — if the list is built by
// splitting and the detail matched exactly, every multi-genre track would
// produce a genre row that leads to an empty page.
func (q *Queries) ListAlbumsByGenreWithArtist(ctx context.Context, arg ListAlbumsByGenreWithArtistParams) ([]ListAlbumsByGenreWithArtistRow, error) {
	rows, err := q.db.Query(ctx, listAlbumsByGenreWithArtist, arg.Genre, arg.Off, arg.Lim)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListAlbumsByGenreWithArtistRow
	for rows.Next() {
		var i ListAlbumsByGenreWithArtistRow
		if err := rows.Scan(
			&i.Album.ID,
			&i.Album.Title,
			&i.Album.SortTitle,
			&i.Album.ArtistID,
			&i.Album.ReleaseDate,
			&i.Album.Mbid,
			&i.Album.CoverArtPath,
			&i.Album.CreatedAt,
			&i.Album.UpdatedAt,
			&i.Album.CoverArtSource,
			&i.Album.CoverArtSourcesVersion,
			&i.ArtistName,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listAlbumsByYearRangeWithArtist = `-- name: ListAlbumsByYearRangeWithArtist :many
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, albums.cover_art_sources_version, artists.name AS artist_name
FROM albums
JOIN artists ON artists.id = albums.artist_id
WHERE albums.release_date IS NOT NULL
  AND EXTRACT(YEAR FROM albums.release_date)::int
      BETWEEN $1::int AND $2::int
ORDER BY albums.sort_title, albums.id
LIMIT $4 OFFSET $3
`

type ListAlbumsByYearRangeWithArtistParams struct {
	YearFrom int32
	YearTo   int32
	Off      int32
	Lim      int32
}

type ListAlbumsByYearRangeWithArtistRow struct {
	Album      Album
	ArtistName string
}

// Albums released within an inclusive year range, for the albums-page filter.
func (q *Queries) ListAlbumsByYearRangeWithArtist(ctx context.Context, arg ListAlbumsByYearRangeWithArtistParams) ([]ListAlbumsByYearRangeWithArtistRow, error) {
	rows, err := q.db.Query(ctx, listAlbumsByYearRangeWithArtist,
		arg.YearFrom,
		arg.YearTo,
		arg.Off,
		arg.Lim,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListAlbumsByYearRangeWithArtistRow
	for rows.Next() {
		var i ListAlbumsByYearRangeWithArtistRow
		if err := rows.Scan(
			&i.Album.ID,
			&i.Album.Title,
			&i.Album.SortTitle,
			&i.Album.ArtistID,
			&i.Album.ReleaseDate,
			&i.Album.Mbid,
			&i.Album.CoverArtPath,
			&i.Album.CreatedAt,
			&i.Album.UpdatedAt,
			&i.Album.CoverArtSource,
			&i.Album.CoverArtSourcesVersion,
			&i.ArtistName,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listGenresForAlbum = `-- name: ListGenresForAlbum :many
SELECT DISTINCT trim(g.genre) AS genre
FROM tracks
JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true
WHERE tracks.album_id = $1 AND trim(g.genre) <> ''
  AND tracks.missing_since IS NULL
ORDER BY trim(g.genre)
`

// Distinct genres carried by an album's tracks, for the album detail page's
// quick-jump chips. Split and trimmed identically to ListGenresWithCount, so a
// chip always leads to a page that actually contains this album — the two
// diverging is exactly the bug #367 had to fix in ListAlbumsByGenre.
func (q *Queries) ListGenresForAlbum(ctx context.Context, albumID pgtype.UUID) ([]string, error) {
	rows, err := q.db.Query(ctx, listGenresForAlbum, albumID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []string
	for rows.Next() {
		var genre string
		if err := rows.Scan(&genre); err != nil {
			return nil, err
		}
		items = append(items, genre)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listGenresForArtist = `-- name: ListGenresForArtist :many
SELECT DISTINCT trim(g.genre) AS genre
FROM tracks
JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true
WHERE tracks.artist_id = $1 AND trim(g.genre) <> ''
  AND tracks.missing_since IS NULL
ORDER BY trim(g.genre)
`

// Same, across everything by one artist. Alphabetical rather than by count:
// an artist's genre set is small, and a stable order reads better than a
// frequency ranking nobody asked about.
func (q *Queries) ListGenresForArtist(ctx context.Context, artistID pgtype.UUID) ([]string, error) {
	rows, err := q.db.Query(ctx, listGenresForArtist, artistID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []string
	for rows.Next() {
		var genre string
		if err := rows.Scan(&genre); err != nil {
			return nil, err
		}
		items = append(items, genre)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listGenresWithCount = `-- name: ListGenresWithCount :many

SELECT trim(g.genre) AS genre, COUNT(DISTINCT tracks.id)::bigint AS track_count
FROM tracks
JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true
WHERE trim(g.genre) <> ''
  AND tracks.missing_since IS NULL
GROUP BY trim(g.genre)
ORDER BY track_count DESC, trim(g.genre)
`

type ListGenresWithCountRow struct {
	Genre      string
	TrackCount int64
}

// Every query in this file filters `tracks.missing_since IS NULL` (#2523).
// A row whose file has vanished keeps its genre forever — the scanner walks the
// filesystem, so it never revisits a path that no longer exists — which is how
// pre-#2499 welded genres survived a full re-scan and kept showing in the index.
// Browsing is a way of finding something to play, so a track that cannot play
// should not shape it.
//
// Year queries below join albums only and are deliberately left alone: an album
// is still a real release even if some of its tracks are gone. An album whose
// EVERY track is missing will linger on the year axis; that's a narrower case,
// tracked with the rest of the cleanup work.
// Genre browse index (#367).
//
// Genres live inline on tracks.genre as a delimited string, so this splits on
// the same [;,] pattern already used by recommendation.sql and discover.sql —
// a track tagged "Rock;Pop" must count toward both, and diverging from the
// established pattern here would make the browse surface disagree with what
// the recommendation engine believes the library contains.
//
// trim() but deliberately NO lower(): trimming repairs an artifact of OUR
// splitting ("Rock; Pop" yields " Pop", and showing that as a distinct genre
// would be a bug), whereas case is what the tag actually says. Raw ID3 is
// exposed as-is for v1, so "Rock" and "rock" appear as separate rows.
//
// COUNT(DISTINCT) because a sloppy tag like "Rock;Rock" would otherwise
// inflate its own row.
//
// Ordered by count first: raw ID3 data has a long tail of one-off junk tags,
// so alphabetical would bury the handful of genres an operator actually has a
// library's worth of. Name breaks ties for a stable order.
// Ordered by the expression, not the output alias: `ORDER BY genre` is
// ambiguous between the alias and tracks.genre, and sqlc rejects it.
func (q *Queries) ListGenresWithCount(ctx context.Context) ([]ListGenresWithCountRow, error) {
	rows, err := q.db.Query(ctx, listGenresWithCount)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListGenresWithCountRow
	for rows.Next() {
		var i ListGenresWithCountRow
		if err := rows.Scan(&i.Genre, &i.TrackCount); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}
