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 TestValidatorWarnsIndependentlyForUngroundedTitleAndSummary(t *testing.T) {
	doc := document()
	value := dnd.SceneDescriptionList{Scenes: []dnd.SceneDescription{
		scene("one", "Watchtower arrival", "The group enters the ruined watchtower."),
		scene("two", "Moonlit council", "Whispers linger."),
		scene("three", "The and to", "An or the"),
	}}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.SceneDescriptionList]{Source: doc, Value: value})
	if err != nil || !result.Approved {
		t.Fatalf("Validate() = %#v, %v", result, err)
	}
	wantScopes := []string{"scenes[1].title", "scenes[1].summary", "scenes[2].title", "scenes[2].summary"}
	if len(result.Diagnostics) != 1 || result.Diagnostics[0].OccurrenceCount != len(wantScopes) {
		t.Fatalf("diagnostics = %#v, want %d occurrences", result.Diagnostics, len(wantScopes))
	}
	for index, scope := range wantScopes[:contracts.MaxDiagnosticSamples] {
		item := result.Diagnostics[0].Samples[index]
		if item.Scope != scope || result.Diagnostics[0].ReasonCode != ReasonCode {
			t.Fatalf("sample[%d] = %#v, want scope %q", index, item, scope)
		}
		if len(item.Message) > 4096 || strings.Contains(item.Message, doc.Units[0].Text) {
			t.Fatalf("sample[%d] is not safely bounded: %#v", index, item)
		}
	}
}

func TestValidatorUsesTranscriptOnlyAndDefersInvalidInputs(t *testing.T) {
	doc := document()
	value := dnd.SceneDescriptionList{Scenes: []dnd.SceneDescription{scene("one", "Greencloak", "Greencloak arrives.")}}
	result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.SceneDescriptionList]{
		Source: doc, Value: value,
		References: contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{
			"glossary": {Items: []contracts.ReferenceItem{{Content: []byte("Greencloak: title")}}},
		}},
	})
	if err != nil || len(result.Diagnostics) != 1 || result.Diagnostics[0].OccurrenceCount != 2 {
		t.Fatalf("Validate() = %#v, %v; want transcript-only advisories", result, err)
	}
	malformed, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.SceneDescriptionList]{Source: doc, Value: dnd.SceneDescriptionList{}})
	if err != nil || !malformed.Approved || len(malformed.Diagnostics) != 0 {
		t.Fatalf("malformed Validate() = %#v, %v; want deferral", malformed, err)
	}

	invalidScene := scene("invalid", "Greencloak", "Greencloak arrives.")
	invalidScene.SourceRef.StartUnitID = 99
	invalidSource := dnd.SceneDescriptionList{Scenes: []dnd.SceneDescription{invalidScene}}
	deferred, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.SceneDescriptionList]{Source: doc, Value: invalidSource})
	if err != nil || !deferred.Approved || len(deferred.Diagnostics) != 0 {
		t.Fatalf("invalid-source Validate() = %#v, %v; want deferral", deferred, err)
	}
}

func TestValidatorRegistrationAndPolicyFingerprint(t *testing.T) {
	registry := pipeline.NewValidatorRegistry()
	if err := Register(registry); err != nil {
		t.Fatal(err)
	}
	if _, ok := registry.Spec(Key); !ok {
		t.Fatalf("registry missing %q", Key)
	}
	if got, want := New(Options{}).CheckpointFingerprints(), []pipeline.CheckpointFingerprint{{Name: "policy", Value: policy}}; !reflect.DeepEqual(got, want) {
		t.Fatalf("fingerprints = %#v, want %#v", got, want)
	}
}

func document() *source.SourceDocument {
	return &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1, Text: "The group enters the ruined watchtower."}}}
}

func scene(id, title, summary string) dnd.SceneDescription {
	return dnd.SceneDescription{ID: id, SourceRef: source.SourceRef{SourceID: "session", StartUnitID: 1, EndUnitID: 1}, Kind: dnd.SceneKindNarrative, Title: title, Summary: summary}
}
