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 TestValidatorMatchesTokenSequencesAcrossRanges(t *testing.T) {
	doc := &source.SourceDocument{ID: "session", Units: []source.SourceUnit{
		{ID: 1, Text: "Aria finds a potion,"},
		{ID: 2, Text: "of healing in the chest."},
		{ID: 3, Text: "The group rests."},
	}}
	value := dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: "POTION OF HEALING", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 1, EndUnitID: 1}, {SourceID: doc.ID, StartUnitID: 2, EndUnitID: 2}}},
		{ItemID: "item", Name: "missing-item", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 3, EndUnitID: 3}}},
	}}
	references := contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{
		"glossary": {Items: []contracts.ReferenceItem{{Content: []byte("missing-item")}}},
	}}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.ItemOccurrenceList]{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-item") {
		t.Fatalf("diagnostic = %#v", diagnostic)
	}
}

func TestValidatorDefersMalformedAndUnreadableEvidence(t *testing.T) {
	doc := &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "a potion"}}}
	for _, value := range []dnd.ItemOccurrenceList{
		{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: "", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 1, EndUnitID: 1}}}}},
		{Occurrences: []dnd.ItemOccurrence{{ItemID: "item", Name: "potion", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 99, EndUnitID: 99}}}}},
	} {
		result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.ItemOccurrenceList]{Source: doc, Value: value})
		if err != nil || !result.Approved || len(result.Diagnostics) != 0 {
			t.Fatalf("deferred result = %#v, %v", result, err)
		}
	}
}

func TestValidatorBoundsAdvisoriesAndRegistersPolicy(t *testing.T) {
	count := contracts.MaxDiagnosticSamples + 5
	occurrences := make([]dnd.ItemOccurrence, count)
	for index := range occurrences {
		occurrences[index] = dnd.ItemOccurrence{ItemID: "item", Name: "missing item", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}
	}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.ItemOccurrenceList]{
		Source: &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "nothing useful"}}},
		Value:  dnd.ItemOccurrenceList{Occurrences: occurrences},
	})
	if err != nil || !result.Approved || len(result.Diagnostics) != 1 || result.Diagnostics[0].OccurrenceCount != count || len(result.Diagnostics[0].Samples) != contracts.MaxDiagnosticSamples {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
	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.Fatalf("Register() error = %v", err)
	}
	if _, err := DecodeOptions(map[string]any{"unexpected": true}); err == nil {
		t.Fatal("DecodeOptions() accepted unknown option")
	}
}
