package soundcloud

import (
	"context"
	"strings"
	"testing"

	"github.com/bjarneo/cliamp/resolve"
)

func TestNewFromConfig(t *testing.T) {
	tests := []struct {
		name string
		cfg  Config
		want bool // want non-nil
	}{
		{"default (not enabled)", Config{}, false},
		{"explicitly enabled", Config{Enabled: true}, true},
		{"enabled with user", Config{Enabled: true, User: "alice"}, true},
		{"enabled trims user", Config{Enabled: true, User: "  bob  "}, true},
		{"user without enabled stays nil", Config{User: "alice"}, false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := NewFromConfig(tt.cfg)
			if (got != nil) != tt.want {
				t.Fatalf("NewFromConfig(%+v) non-nil = %v, want %v", tt.cfg, got != nil, tt.want)
			}
			if got != nil && tt.cfg.User != "" && got.user != "bob" && got.user != "alice" {
				if got.user == "" {
					t.Errorf("user not propagated, got empty")
				}
			}
		})
	}
}

func TestProviderName(t *testing.T) {
	p := NewFromConfig(Config{Enabled: true})
	if got := p.Name(); got != "SoundCloud" {
		t.Errorf("Name() = %q, want SoundCloud", got)
	}
}

func TestPlaylistsWithoutUserShowsBrowse(t *testing.T) {
	p := NewFromConfig(Config{Enabled: true})
	pls, err := p.Playlists()
	if err != nil {
		t.Fatalf("Playlists() error = %v", err)
	}
	if len(pls) == 0 {
		t.Fatal("Playlists() returned 0 entries, want curated browse list")
	}
	for _, pl := range pls {
		if pl.Section != "Browse" {
			t.Errorf("playlist %q has Section %q, want Browse", pl.Name, pl.Section)
		}
		if !strings.HasPrefix(pl.ID, "scsearch") {
			t.Errorf("browse playlist %q ID = %q, want scsearch prefix", pl.Name, pl.ID)
		}
	}
}

func TestPlaylistsWithUserShowsOnlyUserEntries(t *testing.T) {
	p := NewFromConfig(Config{Enabled: true, User: "alice"})
	pls, err := p.Playlists()
	if err != nil {
		t.Fatalf("Playlists() error = %v", err)
	}
	if len(pls) != 3 {
		t.Fatalf("Playlists() = %d entries, want 3 (user-only when User is set)", len(pls))
	}

	wantNames := map[string]string{
		"https://soundcloud.com/alice/tracks":  "Tracks",
		"https://soundcloud.com/alice/likes":   "Likes",
		"https://soundcloud.com/alice/reposts": "Reposts",
	}
	for _, pl := range pls {
		want, ok := wantNames[pl.ID]
		if !ok {
			t.Errorf("unexpected playlist ID %q", pl.ID)
			continue
		}
		if pl.Name != want {
			t.Errorf("Playlist %q Name = %q, want %q", pl.ID, pl.Name, want)
		}
		if pl.Section != "alice" {
			t.Errorf("Playlist %q Section = %q, want alice", pl.ID, pl.Section)
		}
	}
}

func TestPlaylistsEscapesUsername(t *testing.T) {
	p := NewFromConfig(Config{Enabled: true, User: "weird name"})
	pls, _ := p.Playlists()
	if len(pls) == 0 {
		t.Fatal("expected non-empty playlists")
	}
	for _, pl := range pls {
		if got, want := pl.ID, "https://soundcloud.com/weird%20name/"; len(got) < len(want) || got[:len(want)] != want {
			t.Errorf("playlist ID %q not URL-escaped", pl.ID)
		}
	}
}

func TestSearchTracksEmptyQuery(t *testing.T) {
	p := NewFromConfig(Config{Enabled: true})
	tracks, err := p.SearchTracks(context.Background(), "   ", 10)
	if err != nil {
		t.Fatalf("SearchTracks(empty) error = %v", err)
	}
	if len(tracks) != 0 {
		t.Errorf("SearchTracks(empty) returned %d tracks, want 0", len(tracks))
	}
}

func TestTracksRejectsEmptyID(t *testing.T) {
	p := NewFromConfig(Config{Enabled: true})
	_, err := p.Tracks("")
	if err == nil {
		t.Fatal("Tracks(\"\") expected error, got nil")
	}
}

func TestConfigIsSet(t *testing.T) {
	if (Config{}).IsSet() {
		t.Error("Config{} should not be set (opt-in)")
	}
	if !(Config{Enabled: true}).IsSet() {
		t.Error("Config{Enabled:true} should report set")
	}
}

func TestNewFromConfigPropagatesCookies(t *testing.T) {
	t.Cleanup(func() { resolve.SetYTDLCookiesFrom("") })

	resolve.SetYTDLCookiesFrom("") // baseline

	NewFromConfig(Config{Enabled: true, CookiesFrom: "firefox"})
	// The exported getter is internal; we verify by side effect: a subsequent
	// resolve call would emit --cookies-from-browser firefox. We can't easily
	// invoke yt-dlp from a unit test, so the smoke test is that the setter
	// accepts our value without panic and the constructor succeeds.
	// (Full plumbing is exercised manually; resolve.ytdlCookiesFrom is private.)
}
