package store

import (
	"reflect"
	"testing"
)

func rr(book, base, reading string, first, occ int) RubyReading {
	return RubyReading{BookID: book, Base: base, Reading: reading, FirstChapter: first, Occurrences: occ}
}

func TestRubyReadingsReplaceAndRead(t *testing.T) {
	s, _ := openTemp(t)
	if err := s.ReplaceRubyReadings("b", []RubyReading{
		rr("b", "漢字", "かんじ", 2, 5),
		rr("b", "東京", "とうきょう", 1, 3),
	}); err != nil {
		t.Fatal(err)
	}
	if err := s.ReplaceRubyReadings("other", []RubyReading{rr("other", "京", "きょう", 1, 1)}); err != nil {
		t.Fatal(err)
	}
	got, err := s.RubyReadingsForBook("b")
	if err != nil {
		t.Fatal(err)
	}
	// Ordered by (first_chapter, base, reading): 東京(ch1) before 漢字(ch2).
	want := []RubyReading{
		rr("b", "東京", "とうきょう", 1, 3),
		rr("b", "漢字", "かんじ", 2, 5),
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("read = %#v, want %#v (other book must not leak)", got, want)
	}
}

func TestRubyReadingsReplaceIsFullAndIdempotent(t *testing.T) {
	s, _ := openTemp(t)
	first := []RubyReading{rr("b", "漢字", "かんじ", 3, 4), rr("b", "海", "うみ", 1, 2)}
	if err := s.ReplaceRubyReadings("b", first); err != nil {
		t.Fatal(err)
	}
	// Re-ingesting the identical aggregate is a no-op (idempotent).
	if err := s.ReplaceRubyReadings("b", first); err != nil {
		t.Fatal(err)
	}
	got, _ := s.RubyReadingsForBook("b")
	if len(got) != 2 {
		t.Fatalf("identical re-replace must not add rows, got %#v", got)
	}
	// A source edit re-ingest: 海 removed entirely, 漢字 moved to ch 1 with a new count.
	// The full replace must DROP the phantom 海 and CONVERGE 漢字 — a prior upsert-only
	// path would have left 海 forever (external-review #6).
	if err := s.ReplaceRubyReadings("b", []RubyReading{rr("b", "漢字", "かんじ", 1, 2)}); err != nil {
		t.Fatal(err)
	}
	got, _ = s.RubyReadingsForBook("b")
	want := []RubyReading{rr("b", "漢字", "かんじ", 1, 2)}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("full replace must drop the removed pair and converge, got %#v", got)
	}
}

func TestRubyReadingsVariantReadingsAreDistinctRows(t *testing.T) {
	// The same base with two readings (variant yomi) are two rows, not a collision.
	s, _ := openTemp(t)
	if err := s.ReplaceRubyReadings("b", []RubyReading{
		rr("b", "海", "うみ", 1, 2),
		rr("b", "海", "かい", 4, 1),
	}); err != nil {
		t.Fatal(err)
	}
	got, err := s.RubyReadingsForBook("b")
	if err != nil {
		t.Fatal(err)
	}
	if len(got) != 2 {
		t.Fatalf("variant readings must be distinct rows, got %#v", got)
	}
}

func TestRubyReadingsReplaceEmptyClears(t *testing.T) {
	// A source that dropped all its furigana must clear the book's rows.
	s, _ := openTemp(t)
	if err := s.ReplaceRubyReadings("b", []RubyReading{rr("b", "漢字", "かんじ", 1, 1)}); err != nil {
		t.Fatal(err)
	}
	if err := s.ReplaceRubyReadings("b", nil); err != nil {
		t.Fatal(err)
	}
	got, _ := s.RubyReadingsForBook("b")
	if len(got) != 0 {
		t.Fatalf("empty replace must clear the book, got %#v", got)
	}
}
