package api

import (
	"net/url"
	"testing"

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

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

func TestUUIDToString(t *testing.T) {
	var u pgtype.UUID
	if err := u.Scan("00112233-4455-6677-8899-aabbccddeeff"); err != nil {
		t.Fatalf("scan: %v", err)
	}
	got := uuidToString(u)
	want := "00112233-4455-6677-8899-aabbccddeeff"
	if got != want {
		t.Errorf("uuidToString = %q, want %q", got, want)
	}

	var zero pgtype.UUID
	if s := uuidToString(zero); s != "" {
		t.Errorf("uuidToString(invalid) = %q, want empty", s)
	}
}

func TestParseUUID(t *testing.T) {
	u, ok := parseUUID("00112233-4455-6677-8899-aabbccddeeff")
	if !ok || !u.Valid {
		t.Fatalf("parseUUID valid: ok=%v valid=%v", ok, u.Valid)
	}
	if _, ok := parseUUID("not-a-uuid"); ok {
		t.Error("parseUUID accepted garbage")
	}
	if _, ok := parseUUID(""); ok {
		t.Error("parseUUID accepted empty string")
	}
}

func TestYearFromDate(t *testing.T) {
	var d pgtype.Date
	if y := yearFromDate(d); y != 0 {
		t.Errorf("yearFromDate(invalid) = %d, want 0", y)
	}
}

func TestDurationMsToSec(t *testing.T) {
	cases := []struct {
		ms   int32
		want int
	}{
		{0, 0},
		{500, 1},  // rounds up
		{499, 0},  // rounds down
		{1500, 2}, // rounds up
		{60000, 60},
	}
	for _, c := range cases {
		if got := durationMsToSec(c.ms); got != c.want {
			t.Errorf("durationMsToSec(%d) = %d, want %d", c.ms, got, c.want)
		}
	}
}

func TestCoverURLAndStreamURL(t *testing.T) {
	var u pgtype.UUID
	if err := u.Scan("00112233-4455-6677-8899-aabbccddeeff"); err != nil {
		t.Fatalf("scan: %v", err)
	}
	if got := coverURL(u); got != "/api/albums/00112233-4455-6677-8899-aabbccddeeff/cover" {
		t.Errorf("coverURL = %q", got)
	}
	if got := streamURL(u); got != "/api/tracks/00112233-4455-6677-8899-aabbccddeeff/stream" {
		t.Errorf("streamURL = %q", got)
	}
}

func TestArtistRefFrom(t *testing.T) {
	var id pgtype.UUID
	_ = id.Scan("00112233-4455-6677-8899-aabbccddeeff")
	a := dbq.Artist{ID: id, Name: "Beatles", SortName: "Beatles"}
	got := artistRefFrom(a, 7)
	if got.ID != "00112233-4455-6677-8899-aabbccddeeff" {
		t.Errorf("ID = %q", got.ID)
	}
	if got.Name != "Beatles" || got.AlbumCount != 7 {
		t.Errorf("ref = %+v", got)
	}
}

func TestAlbumRefFrom(t *testing.T) {
	var aid, artID pgtype.UUID
	_ = aid.Scan("00000000-0000-0000-0000-000000000001")
	_ = artID.Scan("00000000-0000-0000-0000-000000000002")
	var rel pgtype.Date
	_ = rel.Scan("1969-09-26")
	a := dbq.Album{ID: aid, Title: "Abbey Road", ArtistID: artID, ReleaseDate: rel}
	got := albumRefFrom(a, "Beatles", 17, 2820)
	if got.Title != "Abbey Road" || got.ArtistName != "Beatles" {
		t.Errorf("ref = %+v", got)
	}
	if got.TrackCount != 17 || got.DurationSec != 2820 {
		t.Errorf("counts = %+v", got)
	}
	if got.Year != 1969 {
		t.Errorf("year = %d", got.Year)
	}
	if got.CoverURL != "/api/albums/00000000-0000-0000-0000-000000000001/cover" {
		t.Errorf("cover_url = %q", got.CoverURL)
	}
}

func TestTrackRefFrom(t *testing.T) {
	var tid, aid, artID pgtype.UUID
	_ = tid.Scan("00000000-0000-0000-0000-000000000010")
	_ = aid.Scan("00000000-0000-0000-0000-000000000001")
	_ = artID.Scan("00000000-0000-0000-0000-000000000002")
	trackNum := int32(3)
	discNum := int32(1)
	t2 := dbq.Track{
		ID: tid, Title: "Something", AlbumID: aid, ArtistID: artID,
		TrackNumber: &trackNum, DiscNumber: &discNum,
		DurationMs: 183_000,
	}
	got := trackRefFrom(t2, "Abbey Road", "Beatles")
	if got.Title != "Something" || got.AlbumTitle != "Abbey Road" || got.ArtistName != "Beatles" {
		t.Errorf("ref = %+v", got)
	}
	if got.TrackNumber != 3 || got.DiscNumber != 1 {
		t.Errorf("positions = %+v", got)
	}
	if got.DurationSec != 183 {
		t.Errorf("duration = %d, want 183", got.DurationSec)
	}
	if got.StreamURL != "/api/tracks/00000000-0000-0000-0000-000000000010/stream" {
		t.Errorf("stream_url = %q", got.StreamURL)
	}
}

func TestTrackRefFromNilPositions(t *testing.T) {
	var tid, aid, artID pgtype.UUID
	_ = tid.Scan("00000000-0000-0000-0000-000000000010")
	_ = aid.Scan("00000000-0000-0000-0000-000000000001")
	_ = artID.Scan("00000000-0000-0000-0000-000000000002")
	t2 := dbq.Track{ID: tid, AlbumID: aid, ArtistID: artID, DurationMs: 1000}
	got := trackRefFrom(t2, "A", "B")
	if got.TrackNumber != 0 || got.DiscNumber != 0 {
		t.Errorf("nil positions should be 0, got %+v", got)
	}
}

func TestParsePaging(t *testing.T) {
	tests := []struct {
		name       string
		query      url.Values
		wantLimit  int
		wantOffset int
		wantErr    bool
	}{
		{
			name:       "default (empty values)",
			query:      url.Values{},
			wantLimit:  50,
			wantOffset: 0,
			wantErr:    false,
		},
		{
			name:       "limit=0 clamps to 1",
			query:      url.Values{"limit": {"0"}},
			wantLimit:  1,
			wantOffset: 0,
			wantErr:    false,
		},
		{
			name:       "limit=500 clamps to 200",
			query:      url.Values{"limit": {"500"}},
			wantLimit:  200,
			wantOffset: 0,
			wantErr:    false,
		},
		{
			name:       "offset=-5 clamps to 0",
			query:      url.Values{"offset": {"-5"}},
			wantLimit:  50,
			wantOffset: 0,
			wantErr:    false,
		},
		{
			name:       "limit=abc errors",
			query:      url.Values{"limit": {"abc"}},
			wantLimit:  0,
			wantOffset: 0,
			wantErr:    true,
		},
		{
			name:       "offset=xyz errors",
			query:      url.Values{"offset": {"xyz"}},
			wantLimit:  0,
			wantOffset: 0,
			wantErr:    true,
		},
		{
			name:       "normal values",
			query:      url.Values{"limit": {"25"}, "offset": {"100"}},
			wantLimit:  25,
			wantOffset: 100,
			wantErr:    false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotLimit, gotOffset, err := parsePaging(tt.query)
			if (err != nil) != tt.wantErr {
				t.Errorf("parsePaging() err = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotLimit != tt.wantLimit {
				t.Errorf("parsePaging() limit = %d, want %d", gotLimit, tt.wantLimit)
			}
			if gotOffset != tt.wantOffset {
				t.Errorf("parsePaging() offset = %d, want %d", gotOffset, tt.wantOffset)
			}
		})
	}
}
