package sourcerelatedness

import (
	"context"
	"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 TestValidatorUsesOnlyCitedTranscriptEvidence(t *testing.T) {
	value := validEventList()
	references := contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{"npc_registry": {Items: []contracts.ReferenceItem{{Content: []byte("Ashfang")}}}}}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("The party waits."), References: references, Value: value})
	if err != nil || !result.Approved || len(result.Diagnostics) != 1 || result.Diagnostics[0].ReasonCode != ReasonCode || result.Diagnostics[0].Disposition != contracts.DiagnosticDispositionAdvisory {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
}

func TestValidatorAcceptsUnicodeSubjectInCitedEvidence(t *testing.T) {
	value := validEventList()
	value.Events[0].Name = "O'Rin Thorn"
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("o’rin\u2003thorn flees."), Value: value})
	if err != nil || !result.Approved || len(result.Diagnostics) != 0 {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
}

func TestValidatorDefersMalformedValuesAndBoundsAdvisories(t *testing.T) {
	malformed := dnd.EnemyEventList{Events: []dnd.EnemyEvent{{Name: "Ashfang"}}}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("none"), Value: malformed})
	if err != nil || !result.Approved || len(result.Diagnostics) != 0 {
		t.Fatalf("shape deferral = %#v, %v", result, err)
	}
	value := dnd.EnemyEventList{Events: make([]dnd.EnemyEvent, 30)}
	for index := range value.Events {
		value.Events[index] = dnd.EnemyEvent{Name: "Missing", Kind: dnd.EnemyEventKindEngaged, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}
	}
	result, err = New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("none"), Value: value})
	if err != nil || !result.Approved || len(result.Diagnostics) != 1 || result.Diagnostics[0].OccurrenceCount != len(value.Events) || len(result.Diagnostics[0].Samples) != contracts.MaxDiagnosticSamples {
		t.Fatalf("bounded advisories = %#v, %v", result, err)
	}
	registry := pipeline.NewValidatorRegistry()
	if err := Register(registry); err != nil {
		t.Fatal(err)
	}
	if Spec().Key != Key || Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
		t.Fatalf("Spec() = %#v", Spec())
	}
	if got := New(Options{}).CheckpointFingerprints(); len(got) != 1 || got[0].Value != policy {
		t.Fatalf("CheckpointFingerprints() = %#v", got)
	}
	if _, err := DecodeOptions(map[string]any{"unexpected": true}); err == nil {
		t.Fatal("DecodeOptions() accepted unknown option")
	}
}

func validEventList() dnd.EnemyEventList {
	return dnd.EnemyEventList{Events: []dnd.EnemyEvent{{Name: "Ashfang", Kind: dnd.EnemyEventKindFled, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}}}
}

func document(text string) *source.SourceDocument {
	return &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: text}}}
}
