package library

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestToLibraryItemFlattensColumnsAndKeepsPayload(t *testing.T) {
	raw := json.RawMessage(`{
		"Id":"42","Name":"Arrival","Type":"Movie","ProductionYear":2016,
		"CommunityRating":7.9,"Genres":["Science Fiction"," Drama "],
		"Studios":[{"Name":"Paramount"},{"Name":"  "}],
		"DateCreated":"2024-03-01T10:00:00Z",
		"ImageTags":{"Primary":"abc"}
	}`)

	item, ok := toLibraryItem(raw)
	if !ok {
		t.Fatal("expected the item to parse")
	}
	if item.ID != "42" || item.Name != "Arrival" || item.Type != "Movie" {
		t.Fatalf("unexpected columns: %+v", item)
	}
	if *item.ProductionYear != 2016 || *item.CommunityRating != 7.9 {
		t.Fatalf("unexpected numbers: %+v", item)
	}
	// Whitespace-only studio names are dropped, real ones trimmed.
	if len(item.Studios) != 1 || item.Studios[0] != "Paramount" {
		t.Fatalf("unexpected studios: %v", item.Studios)
	}
	if len(item.Genres) != 2 || item.Genres[1] != "Drama" {
		t.Fatalf("genres should be trimmed: %v", item.Genres)
	}
	if item.DateCreated == nil || item.DateCreated.Year() != 2024 {
		t.Fatalf("unexpected date: %v", item.DateCreated)
	}
	// The payload must survive byte-identical: it is what the TV receives, and it holds
	// fields (image tags, overview) that no column models.
	if string(item.Payload) != string(raw) {
		t.Fatal("payload was altered")
	}
}

func TestToLibraryItemRejectsUnusableRows(t *testing.T) {
	for name, raw := range map[string]string{
		"malformed": `{"Id":`,
		"no id":     `{"Name":"Nameless"}`,
	} {
		if _, ok := toLibraryItem(json.RawMessage(raw)); ok {
			t.Fatalf("%s should have been rejected", name)
		}
	}
}

func TestSearchTextIncludesSeriesNameSoEpisodesAreFindable(t *testing.T) {
	year := 2022
	text := searchText(syncItem{
		Name:           "Good News About Hell",
		Type:           "Episode",
		SeriesName:     "Severance",
		ProductionYear: &year,
		Genres:         []string{"Drama", "Thriller"},
	})

	for _, want := range []string{"Good News About Hell", "Severance", "2022", "Drama"} {
		if !strings.Contains(text, want) {
			t.Fatalf("search text %q is missing %q", text, want)
		}
	}
}

func TestSearchTextIncludesStudiosSoAStudioNameFindsItsTitles(t *testing.T) {
	text := searchText(syncItem{
		Name: "Everything Everywhere All at Once",
		Type: "Movie",
		Studios: []struct {
			Name string `json:"Name"`
		}{{Name: "A24"}, {Name: ""}},
	})

	if !strings.Contains(text, "A24") {
		t.Fatalf("search text %q is missing the studio", text)
	}
	if strings.Contains(text, "  ") {
		t.Fatalf("an unnamed studio should be skipped, not joined as a gap: %q", text)
	}
}

func TestSearchTextDoesNotRepeatTheTitleForAMovie(t *testing.T) {
	text := searchText(syncItem{Name: "Dune", Type: "Movie", SeriesName: "Dune"})

	if strings.Count(strings.ToLower(text), "dune") != 1 {
		t.Fatalf("title should appear once, got %q", text)
	}
}
