package resolve

import (
	"path/filepath"
	"strings"
	"testing"
)

func TestParseM3UBasic(t *testing.T) {
	input := `#EXTM3U
#EXTINF:120,Artist - Song One
http://example.com/song1.mp3
#EXTINF:180,Artist - Song Two
http://example.com/song2.mp3
`
	entries, err := parseM3U(strings.NewReader(input), "")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}

	if len(entries) != 2 {
		t.Fatalf("got %d entries, want 2", len(entries))
	}

	if entries[0].Title != "Artist - Song One" {
		t.Errorf("entry[0].Title = %q, want %q", entries[0].Title, "Artist - Song One")
	}
	if entries[0].Duration != 120 {
		t.Errorf("entry[0].Duration = %d, want 120", entries[0].Duration)
	}
	if entries[0].Path != "http://example.com/song1.mp3" {
		t.Errorf("entry[0].Path = %q", entries[0].Path)
	}

	if entries[1].Title != "Artist - Song Two" {
		t.Errorf("entry[1].Title = %q", entries[1].Title)
	}
	if entries[1].Duration != 180 {
		t.Errorf("entry[1].Duration = %d, want 180", entries[1].Duration)
	}
}

func TestParseM3UNoHeader(t *testing.T) {
	// M3U without #EXTM3U header should still work
	input := `http://example.com/stream1.mp3
http://example.com/stream2.mp3
`
	entries, err := parseM3U(strings.NewReader(input), "")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if len(entries) != 2 {
		t.Fatalf("got %d entries, want 2", len(entries))
	}
	if entries[0].Duration != -1 {
		t.Errorf("bare entry Duration = %d, want -1", entries[0].Duration)
	}
}

func TestParseM3UBOM(t *testing.T) {
	// UTF-8 BOM prefix should be stripped
	input := "\xef\xbb\xbf#EXTM3U\n#EXTINF:60,Song\nhttp://example.com/song.mp3\n"
	entries, err := parseM3U(strings.NewReader(input), "")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if len(entries) != 1 {
		t.Fatalf("got %d entries, want 1", len(entries))
	}
	if entries[0].Title != "Song" {
		t.Errorf("Title = %q, want Song", entries[0].Title)
	}
}

func TestParseM3USkipsComments(t *testing.T) {
	input := `#EXTM3U
# This is a comment
#EXTINF:60,Song
http://example.com/song.mp3
#EXTVLCOPT:some-option
`
	entries, err := parseM3U(strings.NewReader(input), "")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if len(entries) != 1 {
		t.Fatalf("got %d entries, want 1", len(entries))
	}
}

func TestParseM3URelativePaths(t *testing.T) {
	input := `#EXTM3U
#EXTINF:60,Song
music/song.mp3
`
	entries, err := parseM3U(strings.NewReader(input), "/home/user")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if len(entries) != 1 {
		t.Fatalf("got %d entries, want 1", len(entries))
	}
	if entries[0].Path != "/home/user/music/song.mp3" {
		t.Errorf("Path = %q, want /home/user/music/song.mp3", entries[0].Path)
	}
}

func TestParseM3UAbsolutePaths(t *testing.T) {
	input := "/absolute/path/song.mp3\n"
	entries, err := parseM3U(strings.NewReader(input), "/home/user")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if entries[0].Path != "/absolute/path/song.mp3" {
		t.Errorf("Path = %q, want /absolute/path/song.mp3", entries[0].Path)
	}
}

func TestParseM3UEmpty(t *testing.T) {
	entries, err := parseM3U(strings.NewReader(""), "")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if len(entries) != 0 {
		t.Fatalf("got %d entries, want 0", len(entries))
	}
}

func TestParseM3URadioStream(t *testing.T) {
	// Radio stream with -1 duration
	input := `#EXTM3U
#EXTINF:-1,Radio Station
http://radio.example.com/stream
`
	entries, err := parseM3U(strings.NewReader(input), "")
	if err != nil {
		t.Fatalf("parseM3U error: %v", err)
	}
	if len(entries) != 1 {
		t.Fatalf("got %d entries, want 1", len(entries))
	}
	if entries[0].Duration != -1 {
		t.Errorf("Duration = %d, want -1", entries[0].Duration)
	}
	if entries[0].Title != "Radio Station" {
		t.Errorf("Title = %q, want Radio Station", entries[0].Title)
	}
}

func TestM3UEntryToTrackStream(t *testing.T) {
	e := m3uEntry{
		Path:     "http://radio.example.com/stream",
		Title:    "Radio Station",
		Duration: -1,
	}
	tr := m3uEntryToTrack(e)

	if !tr.Stream {
		t.Error("Stream should be true for URL")
	}
	if !tr.Realtime {
		t.Error("Realtime should be true for stream with -1 duration")
	}
	if tr.DurationSecs != 0 {
		t.Errorf("DurationSecs = %d, want 0 (negative clamped)", tr.DurationSecs)
	}
	if tr.Title != "Radio Station" {
		t.Errorf("Title = %q, want Radio Station", tr.Title)
	}
}

func TestM3UEntryToTrackFile(t *testing.T) {
	e := m3uEntry{
		Path:     "/home/user/song.mp3",
		Title:    "My Song",
		Duration: 180,
	}
	tr := m3uEntryToTrack(e)

	if tr.Stream {
		t.Error("Stream should be false for local file")
	}
	if tr.Realtime {
		t.Error("Realtime should be false for local file")
	}
	if tr.DurationSecs != 180 {
		t.Errorf("DurationSecs = %d, want 180", tr.DurationSecs)
	}
}

func TestResolveM3UPathWindows(t *testing.T) {
	tests := []struct {
		name    string
		baseDir string
		entry   string
		want    string
	}{
		{
			name:    "relative backslash",
			baseDir: `C:\Music`,
			entry:   `artist\song.mp3`,
			want:    filepath.Clean(filepath.Join(`C:\Music`, filepath.FromSlash(`artist\song.mp3`))),
		},
		{
			name:    "relative forward slash",
			baseDir: `C:\Music`,
			entry:   `artist/song.mp3`,
			want:    filepath.Clean(filepath.Join(`C:\Music`, filepath.FromSlash(`artist/song.mp3`))),
		},
		{
			name:    "absolute drive-letter",
			baseDir: `C:\Music`,
			entry:   `D:\Other\track.mp3`,
			want:    filepath.Clean(`D:\Other\track.mp3`),
		},
		{
			name:    "absolute UNC path",
			baseDir: `\\server\share`,
			entry:   `sub\file.mp3`,
			want:    filepath.Clean(filepath.Join(`\\server\share`, filepath.FromSlash(`sub\file.mp3`))),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := resolveM3UPath(tt.baseDir, tt.entry)
			if got != tt.want {
				t.Fatalf("resolveM3UPath(%q, %q) = %q, want %q", tt.baseDir, tt.entry, got, tt.want)
			}
		})
	}
}
