package sourcerelatedness

import (
	"context"
	"reflect"
	"strings"
	"testing"

	"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 TestValidatorUsesOnlyCurrentTranscriptAndWarnsOncePerOccurrence(t *testing.T) {
	value := dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{
		{NPCID: "npc:test", Name: "O'Rin Thorn", Kind: dnd.NPCOccurrenceKindDialogue, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}},
		{NPCID: "npc:test", Name: "Missing\nNPC", Kind: dnd.NPCOccurrenceKindMentioned, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 2, EndUnitID: 2}, {SourceID: "session", StartUnitID: 2, EndUnitID: 2}}},
	}}
	doc := &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "O’Rin Thorn speaks."}, {ID: 2, Text: "The party waits."}}}
	references := contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{"glossary": {Items: []contracts.ReferenceItem{{Content: []byte("Missing NPC")}}}}}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{Source: doc, References: references, Value: value})
	if err != nil || !result.Approved || len(result.Diagnostics) != 1 {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
	if diagnostic := result.Diagnostics[0]; diagnostic.Samples[0].Scope != "occurrences[1]" || diagnostic.ReasonCode != ReasonCode || !strings.Contains(diagnostic.Samples[0].Message, `Missing\nNPC`) {
		t.Fatalf("diagnostic = %#v", diagnostic)
	}
}

func TestValidatorDefersMalformedShapeAndInvalidRanges(t *testing.T) {
	for _, value := range []dnd.NPCOccurrenceList{
		{Occurrences: []dnd.NPCOccurrence{{NPCID: "npc:test", Name: "Mira Thorn"}}},
		{Occurrences: []dnd.NPCOccurrence{{NPCID: "npc:test", Name: "Mira Thorn", Kind: dnd.NPCOccurrenceKindDialogue, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 99, EndUnitID: 99}}}}},
	} {
		result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{Source: &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1}}}, Value: value})
		if err != nil || !result.Approved || len(result.Diagnostics) != 0 {
			t.Fatalf("deferral = %#v, %v", result, err)
		}
	}
	value := dnd.NPCOccurrenceList{Occurrences: []dnd.NPCOccurrence{{NPCID: "npc:test", Name: "Mira Thorn", Kind: dnd.NPCOccurrenceKindDialogue, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}}}
	before := value
	_, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{Source: &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "Mira Thorn"}}}, Value: value})
	if err != nil || !reflect.DeepEqual(value, before) {
		t.Fatalf("Validate() mutated value: %#v", value)
	}
	registry := pipeline.NewValidatorRegistry()
	if err := Register(registry); err != nil {
		t.Fatal(err)
	}
}

func TestValidatorBoundsAdvisories(t *testing.T) {
	count := contracts.MaxDiagnosticSamples + 5
	occurrences := make([]dnd.NPCOccurrence, count)
	for index := range occurrences {
		occurrences[index] = dnd.NPCOccurrence{
			NPCID:      "npc:test",
			Name:       "Missing NPC",
			Kind:       dnd.NPCOccurrenceKindMentioned,
			SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}},
		}
	}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.NPCOccurrenceList]{
		Source: &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "The party waits."}}},
		Value:  dnd.NPCOccurrenceList{Occurrences: occurrences},
	})
	if err != nil || !result.Approved {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
	if len(result.Diagnostics) != 1 || result.Diagnostics[0].OccurrenceCount != count || len(result.Diagnostics[0].Samples) != contracts.MaxDiagnosticSamples {
		t.Fatalf("diagnostics = %#v", result.Diagnostics)
	}
}
