package recommendation

import (
	"context"
	"testing"
	"time"

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

	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)

func insertPlayEventDevice(
	t *testing.T, pool *pgxpool.Pool, userID, trackID pgtype.UUID, startedAt time.Time, device string,
) {
	t.Helper()
	ctx := context.Background()
	var sessionID pgtype.UUID
	if err := pool.QueryRow(ctx,
		`INSERT INTO play_sessions (user_id, started_at, last_event_at, client_id)
		 VALUES ($1, $2, $2, 'ctx-dev-test') RETURNING id`,
		userID, startedAt).Scan(&sessionID); err != nil {
		t.Fatalf("insert play_session: %v", err)
	}
	if _, err := pool.Exec(ctx,
		`INSERT INTO play_events (user_id, track_id, session_id, started_at, was_skipped, device_class)
		 VALUES ($1, $2, $3, $4, false, $5)`,
		userID, trackID, sessionID, startedAt, device); err != nil {
		t.Fatalf("insert play_event: %v", err)
	}
}

// TestLoadContextAffinity_DeviceNarrowing seeds one artist played only on
// mobile and another only on web (all in the current time cell), and verifies
// the current-device parameter narrows the affinity cell (#1551): on 'mobile'
// the mobile artist out-scores the web artist and vice-versa, while the
// device-agnostic (”) pass treats them equally.
func TestLoadContextAffinity_DeviceNarrowing(t *testing.T) {
	pool := newPool(t)
	ctx := context.Background()
	q := dbq.New(pool)

	u := seedUser(t, pool, "ctx-dev")
	aMobile := seedArtist(t, pool, "MobileArtist", "")
	aWeb := seedArtist(t, pool, "WebArtist", "")
	alM := seedAlbumForArtist(t, pool, aMobile.ID, "AlbM")
	tM := seedTrackOnAlbum(t, pool, alM.ID, aMobile.ID, "TrkM")
	alW := seedAlbumForArtist(t, pool, aWeb.ID, "AlbW")
	tW := seedTrackOnAlbum(t, pool, alW.ID, aWeb.ID, "TrkW")

	now := time.Now()
	for i := 0; i < 5; i++ {
		insertPlayEventDevice(t, pool, u.ID, tM.ID, now, "mobile")
		insertPlayEventDevice(t, pool, u.ID, tW.ID, now, "web")
	}

	mob, err := LoadContextAffinity(ctx, q, u.ID, "mobile")
	if err != nil {
		t.Fatalf("mobile: %v", err)
	}
	if mob.Affinity(aMobile.ID) <= mob.Affinity(aWeb.ID) {
		t.Errorf("on mobile, mobile artist (%.3f) should out-score web artist (%.3f)",
			mob.Affinity(aMobile.ID), mob.Affinity(aWeb.ID))
	}

	web, err := LoadContextAffinity(ctx, q, u.ID, "web")
	if err != nil {
		t.Fatalf("web: %v", err)
	}
	if web.Affinity(aWeb.ID) <= web.Affinity(aMobile.ID) {
		t.Errorf("on web, web artist (%.3f) should out-score mobile artist (%.3f)",
			web.Affinity(aWeb.ID), web.Affinity(aMobile.ID))
	}

	// Device-agnostic ('' ): all plays fall in the same time cell, so the two
	// artists are treated identically — the device dimension is what separates
	// them above.
	agn, err := LoadContextAffinity(ctx, q, u.ID, "")
	if err != nil {
		t.Fatalf("agnostic: %v", err)
	}
	if agn.Affinity(aMobile.ID) != agn.Affinity(aWeb.ID) {
		t.Errorf("device-agnostic affinities should match, got mobile=%.3f web=%.3f",
			agn.Affinity(aMobile.ID), agn.Affinity(aWeb.ID))
	}
}
