package terminology

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"testing"

	"textmachine/backend/internal/lang"
)

type pyDump struct {
	Roster []struct {
		Src      string          `json:"src"`
		Type     string          `json:"type"`
		Origin   string          `json:"origin"`
		Freq     int             `json:"freq"`
		Drafts   [][]interface{} `json:"drafts"`
		Ctx      []string        `json:"ctx"`
		Evidence []string        `json:"evidence"`
	} `json:"roster"`
	Blocks     map[string]string   `json:"blocks"`
	Batches    [][]string          `json:"batches"`
	Series     map[string][]string `json:"series"`
	BatchRunes int                 `json:"batch_runes"`
}

// TestPremisePythonRigMatchesEngine доказывает ИСПОЛНЕНИЕМ, что питон-риг пробы C
// (consilium_probe.block_of / batches_of) воспроизводит движковые RenderBatch/Batch.
func TestPremisePythonRigMatchesEngine(t *testing.T) {
	raw, err := os.ReadFile("/home/ubuntu/projects/textmachine/eval/premise_review/py_batching.json")
	if err != nil {
		t.Fatal(err)
	}
	var d pyDump
	if err := json.Unmarshal(raw, &d); err != nil {
		t.Fatal(err)
	}

	cands := make([]Candidate, 0, len(d.Roster))
	for _, r := range d.Roster {
		c := Candidate{
			Key: r.Src, Src: r.Src, Type: r.Type, Origin: Origin(r.Origin),
			Freq: r.Freq, SinceCh: 0, Evidence: r.Evidence, KWIC: r.Ctx,
		}
		for _, v := range r.Drafts {
			if len(v) != 2 {
				t.Fatalf("draft shape %v", v)
			}
			c.Variants = append(c.Variants, Variant{
				Dst: v[0].(string), Chunks: int(v[1].(float64)),
			})
		}
		cands = append(cands, c)
	}

	// (1) поблочный рендер
	blockMismatch := 0
	for _, c := range cands {
		got := RenderBatch([]Candidate{c})
		want, ok := d.Blocks[c.Src]
		if !ok {
			t.Fatalf("нет питон-блока для %s", c.Src)
		}
		if got != want {
			blockMismatch++
			if blockMismatch <= 3 {
				t.Errorf("БЛОК РАСХОДИТСЯ %s:\n--- go ---\n%s\n--- py ---\n%s", c.Src, got, want)
			}
		}
	}
	t.Logf("блоков сверено: %d, расхождений: %d", len(cands), blockMismatch)

	// (2) движок ключ-сортирует кандидатов ДО Batch (terminology.go:212 Merge) — риг делает то же.
	sort.Slice(cands, func(i, j int) bool { return cands[i].Key < cands[j].Key })

	// (2а) серии движок выводит DetectSeries из язык-слоя, а риг задаёт руками. Сверяем группировки.
	sEnabled, headFinal := lang.SeriesMorphology("zh")
	auto := DetectSeries(cands, SeriesParams{Enabled: sEnabled, HeadFinal: headFinal})
	hand := map[string]int{}
	i := 0
	for _, name := range []string{"zhuan", "deng", "dengzizhi", "jie"} {
		i++
		for _, m := range d.Series[name] {
			hand[m] = i
		}
	}
	groups := func(m map[string]int) map[int][]string {
		g := map[int][]string{}
		for _, c := range cands {
			if id := m[c.Key]; id != 0 {
				g[id] = append(g[id], c.Key)
			}
		}
		return g
	}
	ga, gh := groups(auto), groups(hand)
	t.Logf("DetectSeries(zh, enabled=%v headFinal=%v): %d серий", sEnabled, headFinal, len(ga))
	for id, mem := range ga {
		t.Logf("  движок серия %d: %v", id, mem)
	}
	for id, mem := range gh {
		t.Logf("  риг    серия %d: %v", id, mem)
	}

	got := Batch(cands, d.BatchRunes, auto)
	gotComp := make([][]string, 0, len(got))
	for _, b := range got {
		row := make([]string, 0, len(b))
		for _, c := range b {
			row = append(row, c.Src)
		}
		gotComp = append(gotComp, row)
	}
	gj, _ := json.Marshal(gotComp)
	pj, _ := json.Marshal(d.Batches)
	if string(gj) != string(pj) {
		t.Errorf("СОСТАВ БАТЧЕЙ РАСХОДИТСЯ:\n go=%s\n py=%s", gj, pj)
	} else {
		sizes := ""
		for _, b := range gotComp {
			sizes += fmt.Sprintf(" %d", len(b))
		}
		t.Logf("состав батчей совпал: %d батчей, размеры%s", len(gotComp), sizes)
	}

	// (3) рунный размер каждого батча против потолка
	for k, b := range got {
		t.Logf("батч %d: %d кандидатов, %d рун (потолок %d)", k, len(b), BatchRunes(b), d.BatchRunes)
	}
}
