package build_test

import (
	"os"
	"path/filepath"
	"slices"
	"testing"

	"chirami/internal/dict"
	"chirami/internal/dict/build"
)

func TestBuildFromFixtures(t *testing.T) {
	jmdictFile, err := os.Open("../testdata/jmdict.json")
	if err != nil {
		t.Fatalf("open jmdict: %v", err)
	}
	defer jmdictFile.Close()

	kanjidicFile, err := os.Open("../testdata/kanjidic.json")
	if err != nil {
		t.Fatalf("open kanjidic: %v", err)
	}
	defer kanjidicFile.Close()

	accentsFile, err := os.Open("../testdata/accents.txt")
	if err != nil {
		t.Fatalf("open accents: %v", err)
	}
	defer accentsFile.Close()

	xmlFile, err := os.Open("../testdata/jmdict.xml")
	if err != nil {
		t.Fatalf("open jmdict.xml: %v", err)
	}
	defer xmlFile.Close()

	furiFile, err := os.Open("../testdata/furigana.json")
	if err != nil {
		t.Fatalf("open furigana.json: %v", err)
	}
	defer furiFile.Close()

	kradFile, err := os.Open("../testdata/kradfile.json")
	if err != nil {
		t.Fatalf("open kradfile.json: %v", err)
	}
	defer kradFile.Close()

	radkFile, err := os.Open("../testdata/radkfile.json")
	if err != nil {
		t.Fatalf("open radkfile.json: %v", err)
	}
	defer radkFile.Close()

	outPath := filepath.Join(t.TempDir(), "dict.bin")
	err = build.Build(
		jmdictFile, kanjidicFile, accentsFile, outPath,
		build.WithPriorities(xmlFile),
		build.WithFurigana(furiFile),
		build.WithKradfile(kradFile),
		build.WithRadkfile(radkFile),
	)
	if err != nil {
		t.Fatalf("build: %v", err)
	}

	d, err := dict.Open(outPath)
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	defer d.Close()

	entries := d.Lookup("食べる")
	if len(entries) == 0 {
		t.Fatalf("lookup 食べる returned 0 entries")
	}

	rubies, ok := d.Furigana("食べる", "たべる")
	if !ok {
		t.Fatalf("expected furigana for 食べる")
	}
	want := []dict.Ruby{
		{Text: "食", Ruby: "た"},
		{Text: "べる", Ruby: ""},
	}
	if !slices.Equal(rubies, want) {
		t.Errorf("got %+v, want %+v", rubies, want)
	}

	// Check Components
	comps := d.Components('食')
	if !slices.Equal(comps, []rune{'食'}) {
		t.Errorf("Components('食') = %v, want ['食']", comps)
	}

	// Check KanjiWith
	kw := d.KanjiWith('艮', 5)
	if len(kw) != 3 || kw[0] != '銀' || kw[1] != '限' || kw[2] != '良' {
		t.Errorf("KanjiWith('艮', 5) = %v, want ['銀', '限', '良']", kw)
	}

	// Check ComponentName
	if name := d.ComponentName('⺅'); name != "person" {
		t.Errorf("ComponentName('⺅') = %q, want 'person'", name)
	}
	if name := d.ComponentName('食'); name != "eat" {
		t.Errorf("ComponentName('食') = %q, want 'eat'", name)
	}

	// Check WordsWith
	words := d.WordsWith('食', 5)
	if len(words) < 2 {
		t.Fatalf("WordsWith('食', 5) returned %d entries, want at least 2", len(words))
	}
	if words[0].Kanji[0].Text != "食べる" {
		t.Errorf("words[0] = %q, want '食べる'", words[0].Kanji[0].Text)
	}

	// Check table of 10 kanji (休 花 働 語 読 海 道 家 想 校)
	table := []struct {
		kanji rune
		comps []rune
		names []string
	}{
		{'休', []rune{'⺅', '木'}, []string{"person", "tree"}},
		{'花', []rune{'⺅', '匕', '艹'}, []string{"person", "spoon", "grass"}},
		{'働', []rune{'丨', '一', '⺅', '力', '日', '丿'}, []string{"line", "one", "person", "power", "sun", "slash"}},
		{'語', []rune{'言', '口', '五'}, []string{"words", "mouth", "five"}},
		{'読', []rune{'言', '士', '儿', '冖'}, []string{"words", "scholar", "legs", "cover"}},
		{'海', []rune{'氵', '母', '毋', '乞'}, []string{"water", "mother", "do not", "beg"}},
		{'道', []rune{'辶', '自', '丷', '首'}, []string{"walk", "oneself", "horns", "head"}},
		{'家', []rune{'宀', '豕'}, []string{"roof", "pig"}},
		{'想', []rune{'心', '木', '目'}, []string{"heart", "tree", "eye"}},
		{'校', []rune{'父', '木', '亠'}, []string{"father", "tree", "lid"}},
	}

	for _, tc := range table {
		gotComps := d.Components(tc.kanji)
		if !slices.Equal(gotComps, tc.comps) {
			t.Errorf("Components(%c) = %v (%c), want %v (%c)", tc.kanji, gotComps, gotComps, tc.comps, tc.comps)
		}
		gotNames := make([]string, len(gotComps))
		for i, c := range gotComps {
			gotNames[i] = d.ComponentName(c)
		}
		if !slices.Equal(gotNames, tc.names) {
			t.Errorf("ComponentNames for %c = %v, want %v", tc.kanji, gotNames, tc.names)
		}
	}
}
