package gobackend

import (
	"strings"
	"testing"
	"unicode/utf8"
)

func TestBuildFilenameFromTemplate_WithRawTrackAndDisc(t *testing.T) {
	metadata := map[string]any{
		"title":  "Song Name",
		"artist": "Artist Name",
		"album":  "Album Name",
		"track":  1,
		"disc":   2,
		"year":   "2025",
	}

	formatted := buildFilenameFromTemplate(
		"{artist} - {track} - {track_raw} - d{disc} - d{disc_raw} - {title}",
		metadata,
	)

	expected := "Artist Name - 01 - 1 - d2 - d2 - Song Name"
	if formatted != expected {
		t.Fatalf("expected %q, got %q", expected, formatted)
	}
}

func TestBuildFilenameFromTemplate_RawPlaceholdersEmptyWhenZero(t *testing.T) {
	metadata := map[string]any{
		"title":  "Song Name",
		"artist": "Artist Name",
		"track":  0,
		"disc":   0,
	}

	formatted := buildFilenameFromTemplate("{track_raw}-{disc_raw}-{title}", metadata)
	expected := "--Song Name"
	if formatted != expected {
		t.Fatalf("expected %q, got %q", expected, formatted)
	}
}

func TestBuildFilenameFromTemplate_InlineNumberFormatting(t *testing.T) {
	metadata := map[string]any{
		"track": 3,
		"disc":  2,
	}

	formatted := buildFilenameFromTemplate("{track:1}-{track:02}-{disc:03}", metadata)
	expected := "3-03-002"
	if formatted != expected {
		t.Fatalf("expected %q, got %q", expected, formatted)
	}
}

func TestBuildFilenameFromTemplate_PlaylistPositionFormatting(t *testing.T) {
	metadata := map[string]any{
		"playlist_position": 4,
		"artist":            "Artist Name",
		"title":             "Song Name",
	}

	formatted := buildFilenameFromTemplate(
		"{playlist_position:02} - {artist} - {title}",
		metadata,
	)
	expected := "04 - Artist Name - Song Name"
	if formatted != expected {
		t.Fatalf("expected %q, got %q", expected, formatted)
	}
}

func TestBuildFilenameFromTemplate_QualityVariant(t *testing.T) {
	metadata := map[string]any{
		"artist":  "Artist Name",
		"title":   "Song Name",
		"quality": "HI_RES_LOSSLESS",
	}

	formatted := buildFilenameFromTemplate(
		"{artist} - {title} - {quality}",
		metadata,
	)
	if formatted != "Artist Name - Song Name - HI_RES_LOSSLESS" {
		t.Fatalf("unexpected quality filename: %q", formatted)
	}
}

func TestBuildFilenameFromTemplate_QualityVariantStagingToken(t *testing.T) {
	metadata := map[string]any{
		"artist":          "Artist Name",
		"title":           "Song Name",
		"quality_variant": "qv_12345678",
	}

	formatted := buildFilenameFromTemplate(
		"{artist} - {title} - {quality_variant}",
		metadata,
	)
	if formatted != "Artist Name - Song Name - qv_12345678" {
		t.Fatalf("unexpected quality variant filename: %q", formatted)
	}
}

func TestBuildDownloadFilename_ProvidesRequestedQuality(t *testing.T) {
	filename := buildDownloadFilename(DownloadRequest{
		TrackName:      "Song Name",
		ArtistName:     "Artist Name",
		FilenameFormat: "{artist} - {title} - {quality}",
		Quality:        "LOSSLESS",
		OutputExt:      ".flac",
	})

	if filename != "Artist Name - Song Name - LOSSLESS.flac" {
		t.Fatalf("unexpected download filename: %q", filename)
	}
}

func TestBuildDownloadFilename_ProvidesTraceabilityPlaceholders(t *testing.T) {
	filename := buildDownloadFilename(DownloadRequest{
		TrackName:        "Song Name",
		ArtistName:       "Artist Name",
		ISRC:             "USABC1234567",
		DownloadProvider: "tidal-web",
		ProviderTrackID:  "123456789",
		FilenameFormat:   "{artist} - {title} [{isrc}] [{provider}-{provider_id}]",
		OutputExt:        ".flac",
	})

	expected := "Artist Name - Song Name [USABC1234567] [tidal-web-123456789].flac"
	if filename != expected {
		t.Fatalf("expected %q, got %q", expected, filename)
	}
}

func TestBuildFilenameFromTemplate_TraceabilityAliases(t *testing.T) {
	metadata := map[string]any{
		"provider":    "soundcloud",
		"provider_id": "998877",
	}

	formatted := buildFilenameFromTemplate("{platform}-{id}", metadata)
	if formatted != "soundcloud-998877" {
		t.Fatalf("unexpected alias filename: %q", formatted)
	}
}

func TestBuildFilenameFromTemplate_CleansEmptyTraceabilityDecorations(t *testing.T) {
	metadata := map[string]any{
		"title":    "Song Name",
		"provider": "tidal-web",
	}

	formatted := buildFilenameFromTemplate(
		"{title} [{isrc}] [{provider}-{provider_id}]",
		metadata,
	)
	if formatted != "Song Name [tidal-web]" {
		t.Fatalf("unexpected empty placeholder cleanup: %q", formatted)
	}
}

func TestBuildDownloadFilename_PreservesVariantTokenWhenTruncated(t *testing.T) {
	filename := buildDownloadFilename(DownloadRequest{
		TrackName:      strings.Repeat("Very Long Song ", 30),
		ArtistName:     "Artist Name",
		FilenameFormat: "{artist} - {title} - {quality_variant}",
		QualityVariant: "qv_12345678",
		OutputExt:      ".flac",
	})

	if !strings.Contains(filename, "qv_12345678") {
		t.Fatalf("quality variant token was truncated: %q", filename)
	}
	if len(strings.TrimSuffix(filename, ".flac")) > maxSanitizedFilenameBytes {
		t.Fatalf("filename base exceeds limit: %d bytes", len(filename))
	}
}

func TestBuildFilenameFromTemplate_DateStrftimeFormatting(t *testing.T) {
	metadata := map[string]any{
		"artist":       "Artist Name",
		"title":        "Song Name",
		"release_date": "2024-03-09",
		"track_number": 7,
		"disc_number":  1,
	}

	formatted := buildFilenameFromTemplate(
		"{artist} - {track:02} - {title} - {date:%Y-%m-%d} - {year}",
		metadata,
	)
	expected := "Artist Name - 07 - Song Name - 2024-03-09 - 2024"
	if formatted != expected {
		t.Fatalf("expected %q, got %q", expected, formatted)
	}
}

func TestBuildFilenameFromTemplate_DateStrftimeFormattingWithYearOnly(t *testing.T) {
	metadata := map[string]any{
		"artist": "Artist Name",
		"title":  "Song Name",
		"date":   "2019",
	}

	formatted := buildFilenameFromTemplate("{date:%Y}-{date:%m}-{date:%d}", metadata)
	expected := "2019-01-01"
	if formatted != expected {
		t.Fatalf("expected %q, got %q", expected, formatted)
	}
}

func TestSanitizeFilenameMatchesDesktopSpacingBehavior(t *testing.T) {
	got := sanitizeFilename(`  "Text In Quotes"?%* / Demo  `)
	want := "Text In Quotes % Demo"
	if got != want {
		t.Fatalf("expected %q, got %q", want, got)
	}
}

func TestSanitizeFilenameFallsBackToUnknownWhenEmpty(t *testing.T) {
	got := sanitizeFilename(`<>:"/\|?*`)
	if got != "Unknown" {
		t.Fatalf("expected %q, got %q", "Unknown", got)
	}
}

func TestSanitizeFilenameTruncatesWithoutSplittingUTF8(t *testing.T) {
	got := sanitizeFilename(strings.Repeat("あ", 80))
	if !utf8.ValidString(got) {
		t.Fatalf("sanitizeFilename returned invalid UTF-8: %q", got)
	}
	if len(got) > 200 {
		t.Fatalf("sanitizeFilename length = %d, want <= 200", len(got))
	}
}
