package api

import (
	"encoding/json"
	"testing"

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

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

// These tests pin the wire-format keys for /api/library/sync upserts.
// Without them, sqlc model field-name drift or accidental
// `json.Marshal(rawStruct)` regressions would silently break the
// Flutter client (which reads snake_case keys via _*FromJson helpers
// in flutter_client/lib/cache/sync_controller.dart).

// validUUID is a deterministic test UUID — not a real value, just
// something that pgtype.UUID.Valid will accept.
var validUUID = pgtype.UUID{
	Bytes: [16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10},
	Valid: true,
}

func assertJSONKeys(t *testing.T, label string, b []byte, want []string) {
	t.Helper()
	var m map[string]json.RawMessage
	if err := json.Unmarshal(b, &m); err != nil {
		t.Fatalf("%s: invalid json: %v\nbody: %s", label, err, b)
	}
	for _, k := range want {
		if _, ok := m[k]; !ok {
			t.Errorf("%s: missing key %q in %s", label, k, b)
		}
	}
}

func TestArtistSyncView_WireKeys(t *testing.T) {
	a := dbq.Artist{ID: validUUID, Name: "Aphex Twin", SortName: "Aphex Twin"}
	b, err := json.Marshal(toArtistSyncView(a))
	if err != nil {
		t.Fatal(err)
	}
	assertJSONKeys(t, "artist", b, []string{
		"id", "name", "sort_name", "mbid",
		"artist_thumb_path", "artist_fanart_path",
	})
}

func TestAlbumSyncView_WireKeys(t *testing.T) {
	a := dbq.Album{ID: validUUID, ArtistID: validUUID, Title: "Drukqs", SortTitle: "Drukqs"}
	b, err := json.Marshal(toAlbumSyncView(a))
	if err != nil {
		t.Fatal(err)
	}
	assertJSONKeys(t, "album", b, []string{
		"id", "artist_id", "title", "sort_title",
		"release_date", "cover_art_path", "mbid",
	})
}

func TestTrackSyncView_WireKeys(t *testing.T) {
	tr := dbq.Track{
		ID: validUUID, AlbumID: validUUID, ArtistID: validUUID,
		Title: "Avril 14th", DurationMs: 121_000,
		FilePath: "x", FileFormat: "flac",
	}
	b, err := json.Marshal(toTrackSyncView(tr))
	if err != nil {
		t.Fatal(err)
	}
	assertJSONKeys(t, "track", b, []string{
		"id", "album_id", "artist_id", "title", "duration_ms",
		"track_number", "disc_number", "file_path", "file_format", "genre",
	})
}

func TestPlaylistSyncView_WireKeys(t *testing.T) {
	variant := "discover"
	p := dbq.Playlist{
		ID: validUUID, UserID: validUUID, Name: "Test",
		IsPublic: true, TrackCount: 5, DurationSec: 600,
		SystemVariant: &variant,
	}
	b, err := json.Marshal(toPlaylistSyncView(p))
	if err != nil {
		t.Fatal(err)
	}
	assertJSONKeys(t, "playlist", b, []string{
		"id", "user_id", "name", "description",
		"is_public", "cover_path", "track_count",
		"duration_sec", "system_variant",
	})
	// Sanity: system_variant carries the value, not just present.
	var m map[string]any
	_ = json.Unmarshal(b, &m)
	if got := m["system_variant"]; got != "discover" {
		t.Errorf("system_variant: want 'discover', got %v", got)
	}
}
