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

package dbq

import (
	"context"

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

const listDeepCutsTracks = `-- name: ListDeepCutsTracks :many

WITH affinity_artists AS (
    SELECT artist_id FROM general_likes_artists WHERE user_id = $1
    UNION
    SELECT t.artist_id
      FROM play_events pe JOIN tracks t ON t.id = pe.track_id
     WHERE pe.user_id = $1 AND pe.was_skipped = false
     GROUP BY t.artist_id
    HAVING COUNT(*) >= 5
),
play_counts AS (
    SELECT track_id, COUNT(*) AS c
      FROM play_events
     WHERE user_id = $1 AND was_skipped = false
     GROUP BY track_id
)
SELECT t.id, t.album_id, t.artist_id
  FROM tracks t
  JOIN affinity_artists aa ON aa.artist_id = t.artist_id
  LEFT JOIN play_counts pc ON pc.track_id = t.id
 WHERE COALESCE(pc.c, 0) <= 2
   AND NOT EXISTS (
       SELECT 1 FROM lidarr_quarantine q
        WHERE q.user_id = $1 AND q.track_id = t.id
   )
 ORDER BY md5(t.id::text || $2::text)
 LIMIT 200
`

type ListDeepCutsTracksParams struct {
	UserID  pgtype.UUID
	Column2 string
}

type ListDeepCutsTracksRow struct {
	ID       pgtype.UUID
	AlbumID  pgtype.UUID
	ArtistID pgtype.UUID
}

// Candidate queries for the discovery system-playlist mixes
// (#419-423). Each returns (id, album_id, artist_id) rows; the Go
// producer caps + truncates and converts to rankedCandidate. Daily-
// deterministic md5(id||dateStr) ordering (where used) keeps a mix
// stable within a day while rotating across days; shuffle-on-play
// gives per-play variety on top.
// #419 Deep Cuts: low-play tracks (<=2 plays) from artists the user
// has liked OR played heavily (>=5 non-skip plays across the artist).
// $1 user_id, $2 date string.
func (q *Queries) ListDeepCutsTracks(ctx context.Context, arg ListDeepCutsTracksParams) ([]ListDeepCutsTracksRow, error) {
	rows, err := q.db.Query(ctx, listDeepCutsTracks, arg.UserID, arg.Column2)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListDeepCutsTracksRow
	for rows.Next() {
		var i ListDeepCutsTracksRow
		if err := rows.Scan(&i.ID, &i.AlbumID, &i.ArtistID); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listFirstListensTracks = `-- name: ListFirstListensTracks :many
WITH heard_albums AS (
    SELECT DISTINCT t.album_id
      FROM play_events pe JOIN tracks t ON t.id = pe.track_id
     WHERE pe.user_id = $1
),
played_artists AS (
    SELECT DISTINCT t.artist_id
      FROM play_events pe JOIN tracks t ON t.id = pe.track_id
     WHERE pe.user_id = $1
)
SELECT t.id, t.album_id, t.artist_id
  FROM tracks t
  JOIN albums al ON al.id = t.album_id
 WHERE NOT EXISTS (SELECT 1 FROM heard_albums h WHERE h.album_id = al.id)
   AND NOT EXISTS (
       SELECT 1 FROM lidarr_quarantine q
        WHERE q.user_id = $1 AND q.track_id = t.id
   )
 ORDER BY
   (CASE
      WHEN EXISTS (
        SELECT 1 FROM general_likes_artists gla
         WHERE gla.user_id = $1 AND gla.artist_id = al.artist_id
      ) THEN 0
      WHEN al.artist_id IN (SELECT artist_id FROM played_artists) THEN 1
      ELSE 2
    END),
   al.id, t.disc_number NULLS FIRST, t.track_number NULLS FIRST
 LIMIT 300
`

type ListFirstListensTracksRow struct {
	ID       pgtype.UUID
	AlbumID  pgtype.UUID
	ArtistID pgtype.UUID
}

// #423 First Listens: albums the user has never played any track of.
// Tiered: liked-artist albums first, then played-artist albums, then
// the rest — album-coherent within each tier. Not diversity-capped
// (whole-album discovery). $1 user_id.
func (q *Queries) ListFirstListensTracks(ctx context.Context, userID pgtype.UUID) ([]ListFirstListensTracksRow, error) {
	rows, err := q.db.Query(ctx, listFirstListensTracks, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListFirstListensTracksRow
	for rows.Next() {
		var i ListFirstListensTracksRow
		if err := rows.Scan(&i.ID, &i.AlbumID, &i.ArtistID); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listNewForYouTracks = `-- name: ListNewForYouTracks :many
WITH affinity_artists AS (
    SELECT artist_id FROM general_likes_artists WHERE user_id = $1
    UNION
    SELECT t.artist_id
      FROM play_events pe JOIN tracks t ON t.id = pe.track_id
     WHERE pe.user_id = $1 AND pe.was_skipped = false
     GROUP BY t.artist_id
    HAVING COUNT(*) >= 3
)
SELECT t.id, t.album_id, t.artist_id
  FROM tracks t
  JOIN albums al ON al.id = t.album_id
  JOIN affinity_artists aa ON aa.artist_id = al.artist_id
 WHERE al.created_at >= now() - interval '30 days'
   AND NOT EXISTS (
       SELECT 1 FROM lidarr_quarantine q
        WHERE q.user_id = $1 AND q.track_id = t.id
   )
 ORDER BY al.created_at DESC, t.disc_number NULLS FIRST, t.track_number NULLS FIRST
 LIMIT 200
`

type ListNewForYouTracksRow struct {
	ID       pgtype.UUID
	AlbumID  pgtype.UUID
	ArtistID pgtype.UUID
}

// #421 New for you: tracks from albums added in the last 30 days
// whose artist the user has liked OR played (>=3 non-skip). Album-
// coherent (newest album first, then disc/track) — the producer does
// NOT diversity-cap these; they're meant as whole-album discovery.
// $1 user_id.
func (q *Queries) ListNewForYouTracks(ctx context.Context, userID pgtype.UUID) ([]ListNewForYouTracksRow, error) {
	rows, err := q.db.Query(ctx, listNewForYouTracks, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListNewForYouTracksRow
	for rows.Next() {
		var i ListNewForYouTracksRow
		if err := rows.Scan(&i.ID, &i.AlbumID, &i.ArtistID); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listOnThisDayTracks = `-- name: ListOnThisDayTracks :many
WITH windowed AS (
    SELECT track_id, COUNT(*) AS c
      FROM play_events
     WHERE user_id = $1 AND was_skipped = false
       AND started_at < now() - interval '30 days'
       AND ABS(EXTRACT(DOY FROM started_at) - EXTRACT(DOY FROM now())) <= 10
     GROUP BY track_id
)
SELECT t.id, t.album_id, t.artist_id
  FROM tracks t
  JOIN windowed w ON w.track_id = t.id
 WHERE NOT EXISTS (
       SELECT 1 FROM lidarr_quarantine q
        WHERE q.user_id = $1 AND q.track_id = t.id
   )
 ORDER BY w.c DESC, md5(t.id::text || $2::text)
 LIMIT 200
`

type ListOnThisDayTracksParams struct {
	UserID  pgtype.UUID
	Column2 string
}

type ListOnThisDayTracksRow struct {
	ID       pgtype.UUID
	AlbumID  pgtype.UUID
	ArtistID pgtype.UUID
}

// #422 On This Day: tracks the user played around this calendar date
// (±10 day-of-year) in the past, excluding the very recent (>30 days
// ago) so it's nostalgic, not just "last week". Weighted by how much
// they were played in those windows. Floor relaxed from 60→30 days
// and window ±7→±10 so it surfaces on a months-old library instead
// of needing a full year of history; still skips cleanly (no rows →
// no playlist) when there's no qualifying history yet.
// $1 user_id, $2 date string.
func (q *Queries) ListOnThisDayTracks(ctx context.Context, arg ListOnThisDayTracksParams) ([]ListOnThisDayTracksRow, error) {
	rows, err := q.db.Query(ctx, listOnThisDayTracks, arg.UserID, arg.Column2)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListOnThisDayTracksRow
	for rows.Next() {
		var i ListOnThisDayTracksRow
		if err := rows.Scan(&i.ID, &i.AlbumID, &i.ArtistID); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const listRediscoverTracks = `-- name: ListRediscoverTracks :many
WITH stats AS (
    SELECT pe.track_id, COUNT(*) AS c, MAX(pe.started_at) AS last_at
      FROM play_events pe
     WHERE pe.user_id = $1 AND pe.was_skipped = false
     GROUP BY pe.track_id
),
deep AS (
    SELECT t.id, t.album_id, t.artist_id, s.c, 0 AS tier
      FROM tracks t
      JOIN stats s ON s.track_id = t.id
     WHERE s.c >= 5
       AND s.last_at <= now() - interval '6 months'
       AND NOT EXISTS (
           SELECT 1 FROM lidarr_quarantine q
            WHERE q.user_id = $1 AND q.track_id = t.id
       )
),
shallow AS (
    SELECT t.id, t.album_id, t.artist_id, s.c, 1 AS tier
      FROM tracks t
      JOIN stats s ON s.track_id = t.id
     WHERE s.c >= 5
       AND s.last_at <= now() - interval '30 days'
       AND NOT EXISTS (
           SELECT 1 FROM lidarr_quarantine q
            WHERE q.user_id = $1 AND q.track_id = t.id
       )
)
SELECT id, album_id, artist_id
  FROM (
    SELECT id, album_id, artist_id, c, tier FROM deep
    UNION ALL
    SELECT id, album_id, artist_id, c, tier FROM shallow
     WHERE NOT EXISTS (SELECT 1 FROM deep)
  ) u
 ORDER BY tier, c DESC, id
 LIMIT 200
`

type ListRediscoverTracksRow struct {
	ID       pgtype.UUID
	AlbumID  pgtype.UUID
	ArtistID pgtype.UUID
}

// #420 Rediscover: tracks the user played a lot (>=5 non-skip) but
// has drifted away from. Tiered so a young library still gets a mix:
//
//	tier 0  not played in the last 6 months (true rediscovery)
//	tier 1  (only if tier 0 empty) not played in the last 30 days
//
// Ordered by historical affection. $1 user_id.
func (q *Queries) ListRediscoverTracks(ctx context.Context, userID pgtype.UUID) ([]ListRediscoverTracksRow, error) {
	rows, err := q.db.Query(ctx, listRediscoverTracks, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListRediscoverTracksRow
	for rows.Next() {
		var i ListRediscoverTracksRow
		if err := rows.Scan(&i.ID, &i.AlbumID, &i.ArtistID); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}
