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

package dbq

import (
	"context"

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

const getCurrentSessionVectorForUser = `-- name: GetCurrentSessionVectorForUser :one
SELECT pe.session_vector_at_play
FROM play_events pe
JOIN play_sessions s ON s.id = pe.session_id
WHERE pe.user_id = $1
  AND s.ended_at IS NULL
ORDER BY pe.started_at DESC
LIMIT 1
`

// Returns the session_vector_at_play of the user's most recent play_event
// in a still-active (un-timed-out) session. NoRows means no current vector.
// Joined with play_sessions so closed sessions don't leak stale vectors.
func (q *Queries) GetCurrentSessionVectorForUser(ctx context.Context, userID pgtype.UUID) ([]byte, error) {
	row := q.db.QueryRow(ctx, getCurrentSessionVectorForUser, userID)
	var session_vector_at_play []byte
	err := row.Scan(&session_vector_at_play)
	return session_vector_at_play, err
}

const getMostRecentPlaySessionForUser = `-- name: GetMostRecentPlaySessionForUser :one
SELECT id, user_id, started_at, ended_at, last_event_at, track_count, client_id FROM play_sessions
WHERE user_id = $1
ORDER BY last_event_at DESC
LIMIT 1
`

func (q *Queries) GetMostRecentPlaySessionForUser(ctx context.Context, userID pgtype.UUID) (PlaySession, error) {
	row := q.db.QueryRow(ctx, getMostRecentPlaySessionForUser, userID)
	var i PlaySession
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.StartedAt,
		&i.EndedAt,
		&i.LastEventAt,
		&i.TrackCount,
		&i.ClientID,
	)
	return i, err
}

const getOpenPlayEventForUser = `-- name: GetOpenPlayEventForUser :one
SELECT id, user_id, track_id, session_id, started_at, ended_at, duration_played_ms, completion_ratio, was_skipped, client_id, session_vector_at_play, scrobbled_at, source, pick_kind, device_class FROM play_events
WHERE user_id = $1 AND ended_at IS NULL
ORDER BY started_at DESC
LIMIT 1
`

// Returns the most recent play_event for a user where ended_at IS NULL.
// Used by the auto-close-prior step in playevents.RecordPlayStarted.
func (q *Queries) GetOpenPlayEventForUser(ctx context.Context, userID pgtype.UUID) (PlayEvent, error) {
	row := q.db.QueryRow(ctx, getOpenPlayEventForUser, userID)
	var i PlayEvent
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.TrackID,
		&i.SessionID,
		&i.StartedAt,
		&i.EndedAt,
		&i.DurationPlayedMs,
		&i.CompletionRatio,
		&i.WasSkipped,
		&i.ClientID,
		&i.SessionVectorAtPlay,
		&i.ScrobbledAt,
		&i.Source,
		&i.PickKind,
		&i.DeviceClass,
	)
	return i, err
}

const getPlayEventByID = `-- name: GetPlayEventByID :one
SELECT id, user_id, track_id, session_id, started_at, ended_at, duration_played_ms, completion_ratio, was_skipped, client_id, session_vector_at_play, scrobbled_at, source, pick_kind, device_class FROM play_events WHERE id = $1
`

func (q *Queries) GetPlayEventByID(ctx context.Context, id pgtype.UUID) (PlayEvent, error) {
	row := q.db.QueryRow(ctx, getPlayEventByID, id)
	var i PlayEvent
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.TrackID,
		&i.SessionID,
		&i.StartedAt,
		&i.EndedAt,
		&i.DurationPlayedMs,
		&i.CompletionRatio,
		&i.WasSkipped,
		&i.ClientID,
		&i.SessionVectorAtPlay,
		&i.ScrobbledAt,
		&i.Source,
		&i.PickKind,
		&i.DeviceClass,
	)
	return i, err
}

const getSystemPickKindForTrack = `-- name: GetSystemPickKindForTrack :one
SELECT pt.pick_kind
FROM playlist_tracks pt
JOIN playlists p ON p.id = pt.playlist_id
WHERE p.user_id = $1
  AND p.system_variant = $2
  AND pt.track_id = $3
LIMIT 1
`

type GetSystemPickKindForTrackParams struct {
	UserID        pgtype.UUID
	SystemVariant *string
	TrackID       pgtype.UUID
}

// Looks a track up in the user's CURRENT snapshot of the given system
// variant and returns its pick_kind. Used at play-ingestion time to
// freeze provenance onto the play_event — snapshots rebuild daily, so
// attribution can't be reconstructed at read time (#1249/#1270). No
// row = track not in today's snapshot (caller stores NULL); a row with
// NULL pick_kind = the variant doesn't stamp (yet). songs_like_artist
// is non-singleton (up to 3 mixes/user); a track in two of them takes
// whichever LIMIT 1 hits — acceptable, tier stamps there describe the
// same eligibility ladder.
func (q *Queries) GetSystemPickKindForTrack(ctx context.Context, arg GetSystemPickKindForTrackParams) (*string, error) {
	row := q.db.QueryRow(ctx, getSystemPickKindForTrack, arg.UserID, arg.SystemVariant, arg.TrackID)
	var pick_kind *string
	err := row.Scan(&pick_kind)
	return pick_kind, err
}

const insertPlayEvent = `-- name: InsertPlayEvent :one
INSERT INTO play_events (
    user_id, track_id, session_id, started_at, client_id, source, pick_kind,
    device_class
) VALUES ($1, $2, $3, $4, $5, $6, $7::text,
    $8::text)
RETURNING id, user_id, track_id, session_id, started_at, ended_at, duration_played_ms, completion_ratio, was_skipped, client_id, session_vector_at_play, scrobbled_at, source, pick_kind, device_class
`

type InsertPlayEventParams struct {
	UserID      pgtype.UUID
	TrackID     pgtype.UUID
	SessionID   pgtype.UUID
	StartedAt   pgtype.Timestamptz
	ClientID    *string
	Source      *string
	PickKind    *string
	DeviceClass *string
}

// pick_kind is non-NULL only for system-playlist plays whose track was
// found (with a stamped kind) in the user's live snapshot for that
// variant at ingestion time (#1249, generalized in #1270).
func (q *Queries) InsertPlayEvent(ctx context.Context, arg InsertPlayEventParams) (PlayEvent, error) {
	row := q.db.QueryRow(ctx, insertPlayEvent,
		arg.UserID,
		arg.TrackID,
		arg.SessionID,
		arg.StartedAt,
		arg.ClientID,
		arg.Source,
		arg.PickKind,
		arg.DeviceClass,
	)
	var i PlayEvent
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.TrackID,
		&i.SessionID,
		&i.StartedAt,
		&i.EndedAt,
		&i.DurationPlayedMs,
		&i.CompletionRatio,
		&i.WasSkipped,
		&i.ClientID,
		&i.SessionVectorAtPlay,
		&i.ScrobbledAt,
		&i.Source,
		&i.PickKind,
		&i.DeviceClass,
	)
	return i, err
}

const insertPlaySession = `-- name: InsertPlaySession :one
INSERT INTO play_sessions (user_id, started_at, last_event_at, client_id)
VALUES ($1, $2, $2, $3)
RETURNING id, user_id, started_at, ended_at, last_event_at, track_count, client_id
`

type InsertPlaySessionParams struct {
	UserID    pgtype.UUID
	StartedAt pgtype.Timestamptz
	ClientID  *string
}

func (q *Queries) InsertPlaySession(ctx context.Context, arg InsertPlaySessionParams) (PlaySession, error) {
	row := q.db.QueryRow(ctx, insertPlaySession, arg.UserID, arg.StartedAt, arg.ClientID)
	var i PlaySession
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.StartedAt,
		&i.EndedAt,
		&i.LastEventAt,
		&i.TrackCount,
		&i.ClientID,
	)
	return i, err
}

const insertSkipEvent = `-- name: InsertSkipEvent :one
INSERT INTO skip_events (user_id, track_id, session_id, skipped_at, position_ms)
VALUES ($1, $2, $3, $4, $5)
RETURNING id, user_id, track_id, session_id, skipped_at, position_ms
`

type InsertSkipEventParams struct {
	UserID     pgtype.UUID
	TrackID    pgtype.UUID
	SessionID  pgtype.UUID
	SkippedAt  pgtype.Timestamptz
	PositionMs int32
}

func (q *Queries) InsertSkipEvent(ctx context.Context, arg InsertSkipEventParams) (SkipEvent, error) {
	row := q.db.QueryRow(ctx, insertSkipEvent,
		arg.UserID,
		arg.TrackID,
		arg.SessionID,
		arg.SkippedAt,
		arg.PositionMs,
	)
	var i SkipEvent
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.TrackID,
		&i.SessionID,
		&i.SkippedAt,
		&i.PositionMs,
	)
	return i, err
}

const listRecentSessionTracks = `-- name: ListRecentSessionTracks :many
SELECT t.id, t.title, t.album_id, t.artist_id, t.track_number, t.disc_number, t.duration_ms, t.file_path, t.file_size, t.file_format, t.bitrate, t.mbid, t.genre, t.added_at, t.updated_at, t.tag_source, t.tag_sources_version, t.tag_read_version, t.missing_since FROM tracks t
JOIN play_events pe ON pe.track_id = t.id
WHERE pe.session_id = $1
  AND pe.started_at < $2
ORDER BY pe.started_at DESC
LIMIT $3
`

type ListRecentSessionTracksParams struct {
	SessionID pgtype.UUID
	StartedAt pgtype.Timestamptz
	Limit     int32
}

// Returns up to $3 tracks in session $1 whose play_event started before
// $2, ordered newest-first. Used by playevents.RecordPlayStarted to
// build the session_vector for the just-inserted play_event.
func (q *Queries) ListRecentSessionTracks(ctx context.Context, arg ListRecentSessionTracksParams) ([]Track, error) {
	rows, err := q.db.Query(ctx, listRecentSessionTracks, arg.SessionID, arg.StartedAt, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []Track
	for rows.Next() {
		var i Track
		if err := rows.Scan(
			&i.ID,
			&i.Title,
			&i.AlbumID,
			&i.ArtistID,
			&i.TrackNumber,
			&i.DiscNumber,
			&i.DurationMs,
			&i.FilePath,
			&i.FileSize,
			&i.FileFormat,
			&i.Bitrate,
			&i.Mbid,
			&i.Genre,
			&i.AddedAt,
			&i.UpdatedAt,
			&i.TagSource,
			&i.TagSourcesVersion,
			&i.TagReadVersion,
			&i.MissingSince,
		); err != nil {
			return nil, err
		}
		items = append(items, i)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const touchPlaySessionLastEvent = `-- name: TouchPlaySessionLastEvent :exec
UPDATE play_sessions
SET last_event_at = $2,
    track_count = track_count + 1
WHERE id = $1
`

type TouchPlaySessionLastEventParams struct {
	ID          pgtype.UUID
	LastEventAt pgtype.Timestamptz
}

func (q *Queries) TouchPlaySessionLastEvent(ctx context.Context, arg TouchPlaySessionLastEventParams) error {
	_, err := q.db.Exec(ctx, touchPlaySessionLastEvent, arg.ID, arg.LastEventAt)
	return err
}

const updatePlayEventEnded = `-- name: UpdatePlayEventEnded :one
UPDATE play_events
SET ended_at = $2,
    duration_played_ms = $3,
    completion_ratio = $4,
    was_skipped = $5
WHERE id = $1
RETURNING id, user_id, track_id, session_id, started_at, ended_at, duration_played_ms, completion_ratio, was_skipped, client_id, session_vector_at_play, scrobbled_at, source, pick_kind, device_class
`

type UpdatePlayEventEndedParams struct {
	ID               pgtype.UUID
	EndedAt          pgtype.Timestamptz
	DurationPlayedMs *int32
	CompletionRatio  *float64
	WasSkipped       bool
}

// Closes a play_event by id with the given ended_at, duration, and skip flag.
// completion_ratio is computed from duration_played_ms and the track duration
// (looked up by the caller — sqlc doesn't do joins on UPDATE).
func (q *Queries) UpdatePlayEventEnded(ctx context.Context, arg UpdatePlayEventEndedParams) (PlayEvent, error) {
	row := q.db.QueryRow(ctx, updatePlayEventEnded,
		arg.ID,
		arg.EndedAt,
		arg.DurationPlayedMs,
		arg.CompletionRatio,
		arg.WasSkipped,
	)
	var i PlayEvent
	err := row.Scan(
		&i.ID,
		&i.UserID,
		&i.TrackID,
		&i.SessionID,
		&i.StartedAt,
		&i.EndedAt,
		&i.DurationPlayedMs,
		&i.CompletionRatio,
		&i.WasSkipped,
		&i.ClientID,
		&i.SessionVectorAtPlay,
		&i.ScrobbledAt,
		&i.Source,
		&i.PickKind,
		&i.DeviceClass,
	)
	return i, err
}

const updatePlayEventVector = `-- name: UpdatePlayEventVector :exec
UPDATE play_events
SET session_vector_at_play = $2
WHERE id = $1
`

type UpdatePlayEventVectorParams struct {
	ID                  pgtype.UUID
	SessionVectorAtPlay []byte
}

// Used right after InsertPlayEvent to populate session_vector_at_play
// once the vector has been computed.
func (q *Queries) UpdatePlayEventVector(ctx context.Context, arg UpdatePlayEventVectorParams) error {
	_, err := q.db.Exec(ctx, updatePlayEventVector, arg.ID, arg.SessionVectorAtPlay)
	return err
}
