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

package repository

import (
	"context"
	"time"

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

const artistsOnlyPlayedOnceInYear = `-- name: ArtistsOnlyPlayedOnceInYear :many
SELECT
    a.id AS artist_id,
    a.name,
    COUNT(l.*) AS listen_count
FROM listens l
JOIN artist_tracks at ON at.track_id = l.track_id
JOIN artists_with_name a ON a.id = at.artist_id
WHERE EXTRACT(YEAR FROM l.listened_at) = $1::int AND l.user_id = $2::int
GROUP BY a.id, a.name
HAVING COUNT(*) = 1
`

type ArtistsOnlyPlayedOnceInYearParams struct {
	Year   int32
	UserID int32
}

type ArtistsOnlyPlayedOnceInYearRow struct {
	ArtistID    int32
	Name        string
	ListenCount int64
}

func (q *Queries) ArtistsOnlyPlayedOnceInYear(ctx context.Context, arg ArtistsOnlyPlayedOnceInYearParams) ([]ArtistsOnlyPlayedOnceInYearRow, error) {
	rows, err := q.db.Query(ctx, artistsOnlyPlayedOnceInYear, arg.Year, arg.UserID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ArtistsOnlyPlayedOnceInYearRow
	for rows.Next() {
		var i ArtistsOnlyPlayedOnceInYearRow
		if err := rows.Scan(&i.ArtistID, &i.Name, &i.ListenCount); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getArtistCountInYear = `-- name: GetArtistCountInYear :one
SELECT
    COUNT(DISTINCT at.artist_id) AS artist_count
FROM listens l
JOIN artist_tracks at ON at.track_id = l.track_id
WHERE l.user_id = $1::int
  AND EXTRACT(YEAR FROM l.listened_at) = $2::int
`

type GetArtistCountInYearParams struct {
	UserID int32
	Year   int32
}

func (q *Queries) GetArtistCountInYear(ctx context.Context, arg GetArtistCountInYearParams) (int64, error) {
	row := q.db.QueryRow(ctx, getArtistCountInYear, arg.UserID, arg.Year)
	var artist_count int64
	err := row.Scan(&artist_count)
	return artist_count, err
}

const getArtistWithLongestGapInYear = `-- name: GetArtistWithLongestGapInYear :one
WITH first_listens AS (
    SELECT
        l.user_id,
        at.artist_id,
        MIN(l.listened_at::date) AS first_listen_of_year
    FROM listens l
    JOIN artist_tracks at ON at.track_id = l.track_id
    WHERE EXTRACT(YEAR FROM l.listened_at) = $1::int
    GROUP BY l.user_id, at.artist_id
),
last_listens AS (
    SELECT
        l.user_id,
        at.artist_id,
        MAX(l.listened_at::date) AS last_listen
    FROM listens l
    JOIN artist_tracks at ON at.track_id = l.track_id
    WHERE l.listened_at < $2::date
    GROUP BY l.user_id, at.artist_id
),
comebacks AS (
    SELECT
        f.user_id,
        f.artist_id,
        f.first_listen_of_year,
        p.last_listen,
        (f.first_listen_of_year - p.last_listen) AS gap_days
    FROM first_listens f
    JOIN last_listens p
      ON f.user_id = p.user_id AND f.artist_id = p.artist_id
),
ranked AS (
    SELECT user_id, artist_id, first_listen_of_year, last_listen, gap_days,
        RANK() OVER (PARTITION BY user_id ORDER BY gap_days DESC) AS r
    FROM comebacks
)
SELECT
    c.user_id,
    c.artist_id,
    awn.name AS artist_name,
    c.last_listen,
    c.first_listen_of_year,
    c.gap_days
FROM ranked c
JOIN artists_with_name awn ON awn.id = c.artist_id
WHERE r = 1
`

type GetArtistWithLongestGapInYearParams struct {
	Year           int32
	FirstDayOfYear pgtype.Date
}

type GetArtistWithLongestGapInYearRow struct {
	UserID            int32
	ArtistID          int32
	ArtistName        string
	LastListen        interface{}
	FirstListenOfYear interface{}
	GapDays           int32
}

func (q *Queries) GetArtistWithLongestGapInYear(ctx context.Context, arg GetArtistWithLongestGapInYearParams) (GetArtistWithLongestGapInYearRow, error) {
	row := q.db.QueryRow(ctx, getArtistWithLongestGapInYear, arg.Year, arg.FirstDayOfYear)
	var i GetArtistWithLongestGapInYearRow
	err := row.Scan(
		&i.UserID,
		&i.ArtistID,
		&i.ArtistName,
		&i.LastListen,
		&i.FirstListenOfYear,
		&i.GapDays,
	)
	return i, err
}

const getArtistsWithOnlyOnePlayInYear = `-- name: GetArtistsWithOnlyOnePlayInYear :many
WITH first_artist_plays_in_year AS (
    SELECT
        l.user_id,
        at.artist_id,
        MIN(l.listened_at) AS first_listen
    FROM listens l
    JOIN artist_tracks at ON at.track_id = l.track_id
    WHERE EXTRACT(YEAR FROM l.listened_at) = 2024
      AND NOT EXISTS (
          SELECT 1
          FROM listens l2
          JOIN artist_tracks at2 ON at2.track_id = l2.track_id
          WHERE l2.user_id = l.user_id
            AND at2.artist_id = at.artist_id
            AND l2.listened_at < DATE '2024-01-01'
      )
    GROUP BY l.user_id, at.artist_id
) 
SELECT
    f.user_id,
    f.artist_id,
    f.first_listen, a.name,
    COUNT(l.*) AS total_plays_in_year
FROM first_artist_plays_in_year f
JOIN listens l ON l.user_id = f.user_id
JOIN artist_tracks at ON at.track_id = l.track_id JOIN artists_with_name a ON at.artist_id = a.id
WHERE at.artist_id = f.artist_id
  AND EXTRACT(YEAR FROM l.listened_at) = 2024
GROUP BY f.user_id, f.artist_id, f.first_listen, a.name HAVING COUNT(*) = 1
`

type GetArtistsWithOnlyOnePlayInYearRow struct {
	UserID           int32
	ArtistID         int32
	FirstListen      interface{}
	Name             string
	TotalPlaysInYear int64
}

func (q *Queries) GetArtistsWithOnlyOnePlayInYear(ctx context.Context) ([]GetArtistsWithOnlyOnePlayInYearRow, error) {
	rows, err := q.db.Query(ctx, getArtistsWithOnlyOnePlayInYear)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetArtistsWithOnlyOnePlayInYearRow
	for rows.Next() {
		var i GetArtistsWithOnlyOnePlayInYearRow
		if err := rows.Scan(
			&i.UserID,
			&i.ArtistID,
			&i.FirstListen,
			&i.Name,
			&i.TotalPlaysInYear,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getFirstListenInYear = `-- name: GetFirstListenInYear :one
SELECT 
    l.track_id, l.listened_at, l.client, l.user_id, 
    t.id, t.musicbrainz_id, t.duration, t.release_id, t.title, 
    get_artists_for_track(t.id) as artists 
FROM listens l 
LEFT JOIN tracks_with_title t ON l.track_id = t.id 
WHERE EXTRACT(YEAR FROM l.listened_at) = 2025 
ORDER BY l.listened_at ASC 
LIMIT 1
`

type GetFirstListenInYearRow struct {
	TrackID       int32
	ListenedAt    time.Time
	Client        *string
	UserID        int32
	ID            pgtype.Int4
	MusicBrainzID *uuid.UUID
	Duration      pgtype.Int4
	ReleaseID     pgtype.Int4
	Title         pgtype.Text
	Artists       []byte
}

func (q *Queries) GetFirstListenInYear(ctx context.Context) (GetFirstListenInYearRow, error) {
	row := q.db.QueryRow(ctx, getFirstListenInYear)
	var i GetFirstListenInYearRow
	err := row.Scan(
		&i.TrackID,
		&i.ListenedAt,
		&i.Client,
		&i.UserID,
		&i.ID,
		&i.MusicBrainzID,
		&i.Duration,
		&i.ReleaseID,
		&i.Title,
		&i.Artists,
	)
	return i, err
}

const getListenPercentageInTimeWindowInYear = `-- name: GetListenPercentageInTimeWindowInYear :one
WITH user_listens_in_year AS (
    SELECT
        listened_at
    FROM listens
    WHERE user_id = $1::int
      AND EXTRACT(YEAR FROM listened_at) = $2::int
),
windowed AS (
    SELECT
        COUNT(*) AS in_window
    FROM user_listens_in_year
    WHERE EXTRACT(HOUR FROM listened_at) >= $3::int
      AND EXTRACT(HOUR FROM listened_at) < $4::int
),
total AS (
    SELECT COUNT(*) AS total_listens
    FROM user_listens_in_year
)
SELECT
    w.in_window,
    t.total_listens,
    ROUND((w.in_window::decimal / t.total_listens) * 100, 2) AS percent_of_total
FROM windowed w, total t
`

type GetListenPercentageInTimeWindowInYearParams struct {
	UserID          int32
	Year            int32
	HourWindowStart int32
	HourWindowEnd   int32
}

type GetListenPercentageInTimeWindowInYearRow struct {
	InWindow       int64
	TotalListens   int64
	PercentOfTotal pgtype.Numeric
}

func (q *Queries) GetListenPercentageInTimeWindowInYear(ctx context.Context, arg GetListenPercentageInTimeWindowInYearParams) (GetListenPercentageInTimeWindowInYearRow, error) {
	row := q.db.QueryRow(ctx, getListenPercentageInTimeWindowInYear,
		arg.UserID,
		arg.Year,
		arg.HourWindowStart,
		arg.HourWindowEnd,
	)
	var i GetListenPercentageInTimeWindowInYearRow
	err := row.Scan(&i.InWindow, &i.TotalListens, &i.PercentOfTotal)
	return i, err
}

const getMostReplayedTrackInYear = `-- name: GetMostReplayedTrackInYear :one
WITH ordered_listens AS (
    SELECT
        user_id,
        track_id,
        listened_at,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY listened_at) AS rn
    FROM listens
    WHERE EXTRACT(YEAR FROM listened_at) = $2::int
),
streaks AS (
    SELECT
        user_id,
        track_id,
        listened_at,
        rn,
        ROW_NUMBER() OVER (PARTITION BY user_id, track_id ORDER BY listened_at) AS track_rn
    FROM ordered_listens
),
grouped_streaks AS (
    SELECT
        user_id,
        track_id,
        rn - track_rn AS group_id,
        COUNT(*) AS streak_length
    FROM streaks
    GROUP BY user_id, track_id, rn - track_rn
),
ranked_streaks AS (
    SELECT user_id, track_id, group_id, streak_length,
        RANK() OVER (PARTITION BY user_id ORDER BY streak_length DESC) AS r
    FROM grouped_streaks
)
SELECT
    t.id, t.musicbrainz_id, t.duration, t.release_id, t.title, 
    get_artists_for_track(t.id) as artists,
    streak_length
FROM ranked_streaks rs JOIN tracks_with_title t ON rs.track_id = t.id
WHERE user_id = $1::int AND r = 1
`

type GetMostReplayedTrackInYearParams struct {
	UserID int32
	Year   int32
}

type GetMostReplayedTrackInYearRow struct {
	ID            int32
	MusicBrainzID *uuid.UUID
	Duration      int32
	ReleaseID     int32
	Title         string
	Artists       []byte
	StreakLength  int64
}

func (q *Queries) GetMostReplayedTrackInYear(ctx context.Context, arg GetMostReplayedTrackInYearParams) (GetMostReplayedTrackInYearRow, error) {
	row := q.db.QueryRow(ctx, getMostReplayedTrackInYear, arg.UserID, arg.Year)
	var i GetMostReplayedTrackInYearRow
	err := row.Scan(
		&i.ID,
		&i.MusicBrainzID,
		&i.Duration,
		&i.ReleaseID,
		&i.Title,
		&i.Artists,
		&i.StreakLength,
	)
	return i, err
}

const getPercentageOfTotalListensFromTopArtistsInYear = `-- name: GetPercentageOfTotalListensFromTopArtistsInYear :one
WITH user_artist_listens AS (
    SELECT
        at.artist_id,
        COUNT(*) AS listen_count
    FROM listens l
    JOIN artist_tracks at ON at.track_id = l.track_id
    WHERE l.user_id = $2::int
      AND EXTRACT(YEAR FROM l.listened_at) = $3::int
    GROUP BY at.artist_id
),
top_artists AS (
    SELECT
        artist_id,
        listen_count
    FROM user_artist_listens
    ORDER BY listen_count DESC
    LIMIT $1
),
totals AS (
    SELECT
        (SELECT SUM(listen_count) FROM top_artists) AS top_artist_total,
        (SELECT SUM(listen_count) FROM user_artist_listens) AS overall_total
)
SELECT
    top_artist_total,
    overall_total,
    ROUND((top_artist_total::decimal / overall_total) * 100, 2) AS percent_of_total
FROM totals
`

type GetPercentageOfTotalListensFromTopArtistsInYearParams struct {
	Limit  int32
	UserID int32
	Year   int32
}

type GetPercentageOfTotalListensFromTopArtistsInYearRow struct {
	TopArtistTotal int64
	OverallTotal   int64
	PercentOfTotal pgtype.Numeric
}

func (q *Queries) GetPercentageOfTotalListensFromTopArtistsInYear(ctx context.Context, arg GetPercentageOfTotalListensFromTopArtistsInYearParams) (GetPercentageOfTotalListensFromTopArtistsInYearRow, error) {
	row := q.db.QueryRow(ctx, getPercentageOfTotalListensFromTopArtistsInYear, arg.Limit, arg.UserID, arg.Year)
	var i GetPercentageOfTotalListensFromTopArtistsInYearRow
	err := row.Scan(&i.TopArtistTotal, &i.OverallTotal, &i.PercentOfTotal)
	return i, err
}

const getPercentageOfTotalListensFromTopTracksInYear = `-- name: GetPercentageOfTotalListensFromTopTracksInYear :one
WITH user_listens AS (
    SELECT
        l.track_id,
        COUNT(*) AS listen_count
    FROM listens l
    WHERE l.user_id = $2::int
      AND EXTRACT(YEAR FROM l.listened_at) = $3::int
    GROUP BY l.track_id
),
top_tracks AS (
    SELECT
        track_id,
        listen_count
    FROM user_listens
    ORDER BY listen_count DESC
    LIMIT $1
),
totals AS (
    SELECT
        (SELECT SUM(listen_count) FROM top_tracks) AS top_tracks_total,
        (SELECT SUM(listen_count) FROM user_listens) AS overall_total
)
SELECT
    top_tracks_total,
    overall_total,
    ROUND((top_tracks_total::decimal / overall_total) * 100, 2) AS percent_of_total
FROM totals
`

type GetPercentageOfTotalListensFromTopTracksInYearParams struct {
	Limit  int32
	UserID int32
	Year   int32
}

type GetPercentageOfTotalListensFromTopTracksInYearRow struct {
	TopTracksTotal int64
	OverallTotal   int64
	PercentOfTotal pgtype.Numeric
}

func (q *Queries) GetPercentageOfTotalListensFromTopTracksInYear(ctx context.Context, arg GetPercentageOfTotalListensFromTopTracksInYearParams) (GetPercentageOfTotalListensFromTopTracksInYearRow, error) {
	row := q.db.QueryRow(ctx, getPercentageOfTotalListensFromTopTracksInYear, arg.Limit, arg.UserID, arg.Year)
	var i GetPercentageOfTotalListensFromTopTracksInYearRow
	err := row.Scan(&i.TopTracksTotal, &i.OverallTotal, &i.PercentOfTotal)
	return i, err
}

const getTracksPlayedAtLeastOncePerMonthInYear = `-- name: GetTracksPlayedAtLeastOncePerMonthInYear :many
WITH monthly_plays AS (
    SELECT
        l.track_id,
        EXTRACT(MONTH FROM l.listened_at) AS month
    FROM listens l
    WHERE EXTRACT(YEAR FROM l.listened_at) = $1::int
    GROUP BY l.track_id, EXTRACT(MONTH FROM l.listened_at)
),
monthly_counts AS (
    SELECT
        track_id,
        COUNT(DISTINCT month) AS months_played
    FROM monthly_plays
    GROUP BY track_id
)
SELECT
    t.id AS track_id,
    t.title
FROM monthly_counts mc
JOIN tracks_with_title t ON t.id = mc.track_id
WHERE mc.months_played = 12
`

type GetTracksPlayedAtLeastOncePerMonthInYearRow struct {
	TrackID int32
	Title   string
}

func (q *Queries) GetTracksPlayedAtLeastOncePerMonthInYear(ctx context.Context, userID int32) ([]GetTracksPlayedAtLeastOncePerMonthInYearRow, error) {
	rows, err := q.db.Query(ctx, getTracksPlayedAtLeastOncePerMonthInYear, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetTracksPlayedAtLeastOncePerMonthInYearRow
	for rows.Next() {
		var i GetTracksPlayedAtLeastOncePerMonthInYearRow
		if err := rows.Scan(&i.TrackID, &i.Title); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getWeekWithMostListensInYear = `-- name: GetWeekWithMostListensInYear :one
SELECT
    DATE_TRUNC('week', listened_at + INTERVAL '1 day') - INTERVAL '1 day' AS week_start,
    COUNT(*) AS listen_count
FROM listens 
WHERE EXTRACT(YEAR FROM listened_at) = $1::int 
    AND user_id = $2::int
GROUP BY week_start
ORDER BY listen_count DESC
LIMIT 1
`

type GetWeekWithMostListensInYearParams struct {
	Year   int32
	UserID int32
}

type GetWeekWithMostListensInYearRow struct {
	WeekStart   int32
	ListenCount int64
}

func (q *Queries) GetWeekWithMostListensInYear(ctx context.Context, arg GetWeekWithMostListensInYearParams) (GetWeekWithMostListensInYearRow, error) {
	row := q.db.QueryRow(ctx, getWeekWithMostListensInYear, arg.Year, arg.UserID)
	var i GetWeekWithMostListensInYearRow
	err := row.Scan(&i.WeekStart, &i.ListenCount)
	return i, err
}

const tracksOnlyPlayedOnceInYear = `-- name: TracksOnlyPlayedOnceInYear :many
SELECT
    t.id AS track_id,
    t.title,
    get_artists_for_track(t.id) as artists,
    COUNT(l.*) AS listen_count
FROM listens l
JOIN tracks_with_title t ON t.id = l.track_id
WHERE EXTRACT(YEAR FROM l.listened_at) = $2::int AND l.user_id = $3::int
GROUP BY t.id, t.title
HAVING COUNT(*) = 1
LIMIT $1
`

type TracksOnlyPlayedOnceInYearParams struct {
	Limit  int32
	Year   int32
	UserID int32
}

type TracksOnlyPlayedOnceInYearRow struct {
	TrackID     int32
	Title       string
	Artists     []byte
	ListenCount int64
}

func (q *Queries) TracksOnlyPlayedOnceInYear(ctx context.Context, arg TracksOnlyPlayedOnceInYearParams) ([]TracksOnlyPlayedOnceInYearRow, error) {
	rows, err := q.db.Query(ctx, tracksOnlyPlayedOnceInYear, arg.Limit, arg.Year, arg.UserID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []TracksOnlyPlayedOnceInYearRow
	for rows.Next() {
		var i TracksOnlyPlayedOnceInYearRow
		if err := rows.Scan(
			&i.TrackID,
			&i.Title,
			&i.Artists,
			&i.ListenCount,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}
