package shape

import (
	"context"
	"reflect"
	"strings"
	"testing"
	"unicode/utf8"

	"gitea.maximumdirect.net/eric/notarius/internal/core/source"
	"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
	"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
	"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
)

func TestValidatorOwnsRequiredNameKindAndEvidence(t *testing.T) {
	valid := validList()
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{Value: valid})
	if err != nil || !result.Approved {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
	for _, test := range []struct {
		name  string
		value dnd.NPCOccurrenceList
		want  string
	}{
		{"missing occurrences", dnd.NPCOccurrenceList{}, "occurrences must be present"},
		{"blank name", dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{{NPCID: "npc:test", Name: " ", Kind: dnd.NPCOccurrenceKindDialogue, SourceRefs: valid.Occurrences[0].SourceRefs}}}, "name must not be empty"},
		{"unsupported kind", dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{{NPCID: "npc:test", Name: "Mira Thorn", Kind: "unsupported", SourceRefs: valid.Occurrences[0].SourceRefs}}}, "kind is unsupported"},
		{"missing evidence", dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{{NPCID: "npc:test", Name: "Mira Thorn", Kind: dnd.NPCOccurrenceKindDialogue}}}, "source_refs must contain"},
	} {
		t.Run(test.name, func(t *testing.T) {
			result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{Value: test.value})
			if err != nil || result.Approved || result.ReasonCode != ReasonCode || !strings.Contains(result.Message, test.want) {
				t.Fatalf("Validate() = %#v, %v; want %q", result, err, test.want)
			}
		})
	}
}

func TestValidatorBoundsDiagnosticsPreservesValueAndRegistersTypedContract(t *testing.T) {
	value := dnd.NPCOccurrenceList{Occurrences: make([]dnd.NPCOccurrence, 24)}
	for index := range value.Occurrences {
		value.Occurrences[index] = dnd.NPCOccurrence{NPCID: "npc:test", Name: strings.Repeat("火", 220) + "\n", Kind: "unsupported"}
	}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{Value: value})
	if err != nil || result.Approved || len([]byte(result.Message)) > 4096 || !utf8.ValidString(result.Message) || !strings.Contains(result.Message, "additional issue(s) omitted") {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
	if value.Occurrences[0].Name != strings.Repeat("火", 220)+"\n" {
		t.Fatal("Validate() mutated input")
	}
	if got := New(Options{}).CheckpointFingerprints(); !reflect.DeepEqual(got, []pipeline.CheckpointFingerprint{{Name: "policy", Value: policy}}) {
		t.Fatalf("CheckpointFingerprints() = %#v", got)
	}
	registry := pipeline.NewValidatorRegistry()
	if err := Register(registry); err != nil {
		t.Fatal(err)
	}
	if _, err := DecodeOptions(map[string]any{"unexpected": true}); err == nil {
		t.Fatal("DecodeOptions() accepted unknown option")
	}
}

func validList() dnd.NPCOccurrenceList {
	return dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{{
		NPCID: "npc:test", Name: "Mira Thorn", Kind: dnd.NPCOccurrenceKindDialogue,
		SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}},
	}}}
}
