package services

// Unit tests for Composer base helpers — pure functions, no DB
// dependency (t-paliad-313 Slice A).

import "testing"

func TestFamilyOfCode(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		// canonical four-segment codes → first three segments
		{"de.inf.lg.erwidg", "de.inf.lg"},
		{"de.inf.lg.klage", "de.inf.lg"},
		{"de.inf.olg.berufung", "de.inf.olg"},
		{"upc.inf.cfi.soc", "upc.inf.cfi"},
		{"upc.inf.cfi.sod", "upc.inf.cfi"},
		{"upc.apl.cost.leave_app", "upc.apl.cost"},
		{"epa.opp.opd.einspruch", "epa.opp.opd"},
		// five-segment codes (rarely used in the corpus today) → still
		// truncate to three
		{"upc.inf.cfi.appeal_spawn.followup", "upc.inf.cfi"},
		// shorter codes pass through unchanged
		{"de.inf.lg", "de.inf.lg"},
		{"de.inf", "de.inf"},
		{"de", "de"},
		// empty stays empty
		{"", ""},
	}
	for _, tc := range cases {
		t.Run(tc.in, func(t *testing.T) {
			if got := familyOfCode(tc.in); got != tc.want {
				t.Errorf("familyOfCode(%q) = %q; want %q", tc.in, got, tc.want)
			}
		})
	}
}

func TestBaseSectionSpec_DecodeShape(t *testing.T) {
	// The default seed in mig 146 emits a JSON document the service
	// must decode round-trip; this golden pins the exact field shape
	// the editor expects.
	raw := []byte(`{
		"version": 1,
		"stylemap": {
			"paragraph": "HLpat-Body-B0",
			"heading_1": "HLpat-Heading-H1",
			"heading_2": "HLpat-Heading-H2",
			"heading_3": "HLpat-Heading-H3",
			"list_bullet": "HLpat-Body-B0",
			"list_numbered": "HLpat-Body-B0",
			"blockquote": "HLpat-Body-B1"
		},
		"defaults": [
			{"section_key":"letterhead","kind":"prose","order_index":1,"label_de":"Briefkopf","label_en":"Letterhead","included":true,"seed_md_de":"hi","seed_md_en":"hi"},
			{"section_key":"requests","kind":"requests","order_index":4,"label_de":"Anträge","label_en":"Requests","included":true,"seed_md_de":"","seed_md_en":""}
		]
	}`)
	b := SubmissionBase{SectionSpecRaw: raw}
	if err := b.decode(); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if b.SectionSpec.Version != 1 {
		t.Errorf("Version = %d; want 1", b.SectionSpec.Version)
	}
	if got := b.SectionSpec.Stylemap["heading_1"]; got != "HLpat-Heading-H1" {
		t.Errorf("Stylemap[heading_1] = %q; want HLpat-Heading-H1", got)
	}
	if len(b.SectionSpec.Defaults) != 2 {
		t.Fatalf("Defaults len = %d; want 2", len(b.SectionSpec.Defaults))
	}
	first := b.SectionSpec.Defaults[0]
	if first.SectionKey != "letterhead" || first.Kind != "prose" || first.OrderIndex != 1 {
		t.Errorf("Defaults[0] = %+v; want letterhead/prose/1", first)
	}
	if first.SeedMDDE != "hi" || first.SeedMDEN != "hi" {
		t.Errorf("Defaults[0] seed_md_* = %q/%q; want hi/hi", first.SeedMDDE, first.SeedMDEN)
	}
	second := b.SectionSpec.Defaults[1]
	if second.SectionKey != "requests" || second.Kind != "requests" || second.OrderIndex != 4 {
		t.Errorf("Defaults[1] = %+v; want requests/requests/4", second)
	}
}

func TestBaseSectionSpec_EmptyDecode(t *testing.T) {
	// A bare row (SectionSpecRaw == nil) decodes cleanly into the
	// zero value — no panic, no garbage.
	b := SubmissionBase{}
	if err := b.decode(); err != nil {
		t.Fatalf("decode empty: %v", err)
	}
	if b.SectionSpec.Version != 0 || len(b.SectionSpec.Defaults) != 0 {
		t.Errorf("expected zero SectionSpec on empty raw; got %+v", b.SectionSpec)
	}
	if b.IsDefaultFor == nil {
		t.Errorf("IsDefaultFor must be non-nil (empty slice) after decode; got nil")
	}
}
