package recognize

import (
	"bytes"
	"log/slog"
	"strings"
	"testing"
)

func TestFoldHomoglyphs(t *testing.T) {
	cases := map[string]string{
		// Latin-доминантный токен: кириллический двойник 'а' (U+0430) → 'a'.
		"Hаrold and the Purple Crayon": "Harold and the Purple Crayon",
		// Честная кириллица — не трогаем.
		"Тёмный рыцарь": "Тёмный рыцарь",
		// Честная латиница — не трогаем.
		"The Dark Knight": "The Dark Knight",
		// Cyrillic-доминантный токен (М р з) с латинскими 'o' (U+006F) → 'о'.
		"Мoрoз": "Мороз",
		// Равенство скриптов в токене (кир. О U+041E + лат. k) — не трогаем.
		"Оk": "Оk",
		// Двуязычные, но одно-скриптовые токены — каждый нетронут.
		"Fargo Фарго": "Fargo Фарго",
		// Буква-меньшинство без двойника в таблице (кир. 'б' U+0431) остаётся.
		"Harбld": "Harбld",
		// Пустая строка.
		"": "",
	}
	for in, want := range cases {
		if got := foldHomoglyphs(in); got != want {
			t.Errorf("foldHomoglyphs(%q) = %q, want %q", in, got, want)
		}
	}
}

func TestSanitizeTitle(t *testing.T) {
	cases := map[string]string{
		// Zero-width (U+200B) внутри слова удаляется, слово склеивается.
		"a\u200bb": "ab",
		// Zero-width + двойник + лишние пробелы → strip + fold + collapse + trim.
		"  Hаrold\u200b  Crayon  ": "Harold Crayon",
		// Табы/переводы строк как пробелы, сведены к одиночным.
		"Harold\tand  the": "Harold and the",
		// Управляющий символ (BEL U+0007) удаляется.
		"Harold\u0007": "Harold",
		// BOM (U+FEFF) удаляется.
		"\ufeffFargo": "Fargo",
		// Уже чистое — без изменений.
		"The Matrix": "The Matrix",
	}
	for in, want := range cases {
		if got := SanitizeTitle(in); got != want {
			t.Errorf("SanitizeTitle(%q) = %q, want %q", in, got, want)
		}
	}
}

func TestSanitizePlan_FieldsCleanedSrcUntouched(t *testing.T) {
	// src намеренно содержит кириллический двойник — sanitizePlan его не трогает.
	dirtySrc := "Hаrold.mkv"
	p := Plan{
		Title:         "Hаrold",
		OriginalTitle: "  spaced  ",
		ProviderHint:  "clean\u200b",
		Files:         []PlanFile{{Src: dirtySrc, Role: RoleMain}},
	}
	sanitizePlan(&p, nil)

	if p.Title != "Harold" {
		t.Errorf("Title = %q, want %q", p.Title, "Harold")
	}
	if p.OriginalTitle != "spaced" {
		t.Errorf("OriginalTitle = %q, want %q", p.OriginalTitle, "spaced")
	}
	if p.ProviderHint != "clean" {
		t.Errorf("ProviderHint = %q, want %q", p.ProviderHint, "clean")
	}
	if p.Files[0].Src != dirtySrc {
		t.Errorf("files[].src изменён санитайзингом: %q != %q", p.Files[0].Src, dirtySrc)
	}
}

func TestSanitizePlan_LogsOnChange(t *testing.T) {
	var buf bytes.Buffer
	log := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
	p := Plan{Title: "Hаrold"} // 'а' — кириллица, санитайзинг перепишет
	sanitizePlan(&p, log)
	out := buf.String()
	if !strings.Contains(out, "recognition plan field sanitized") || !strings.Contains(out, "field=title") {
		t.Errorf("ожидался Debug-лог о санитайзинге title, got: %q", out)
	}
}

func TestSanitizePlan_NoLogWhenClean(t *testing.T) {
	var buf bytes.Buffer
	log := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
	p := Plan{Title: "Harold", OriginalTitle: "The Matrix"}
	sanitizePlan(&p, log)
	if buf.Len() != 0 {
		t.Errorf("для чистых полей лога быть не должно, got: %q", buf.String())
	}
}
