package itemoccurrences

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"
	itemcodec "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/codec/itemregistry"
	itemidentity "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/items/identity"
)

func registryReferences(t *testing.T) contracts.ReferenceSet {
	t.Helper()
	content, err := itemcodec.New().Encode(dnd.ItemRegistry{Items: []dnd.Item{{ID: itemidentity.DeriveID("Torch"), Name: "Torch", SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}}})
	if err != nil {
		t.Fatal(err)
	}
	return contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{ItemRegistryReferenceSlot: {Slot: contracts.ReferenceSlot{Name: ItemRegistryReferenceSlot}, Items: []contracts.ReferenceItem{{SlotName: ItemRegistryReferenceSlot, Content: content, MediaType: "application/json", ArtifactKind: dnd.ItemRegistryKind}}}}}
}

func TestNormalizeCanonicalizesRegistryNameAndRetainsUnknownValues(t *testing.T) {
	doc := &source.SourceDocument{ID: "session", Units: []source.SourceUnit{{ID: 1}, {ID: 2}}}
	refs := registryReferences(t)
	normalizer, err := New(Options{}, refs)
	if err != nil {
		t.Fatal(err)
	}
	id := itemidentity.DeriveID("Torch")
	input := dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{
		{ItemID: id, Name: "torch", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 2, EndUnitID: 2}}},
		{ItemID: "unknown", Name: "Unknown", Kind: dnd.ItemOccurrenceKindDiscovered, SourceRefs: []source.SourceRef{{SourceID: doc.ID, StartUnitID: 1, EndUnitID: 1}}},
	}}
	result, err := normalizer.Normalize(context.Background(), contracts.TypedNormalizeRequest[dnd.ItemOccurrenceList]{Source: doc, References: refs, MergeOutput: contracts.MergeArtifact[dnd.ItemOccurrenceList]{Value: input}})
	if err != nil {
		t.Fatal(err)
	}
	if result.Value.Occurrences[1].Name != "Torch" || result.Value.Occurrences[0].Name != "Unknown" {
		t.Fatalf("occurrences = %#v", result.Value.Occurrences)
	}
	if !hasDiagnostic(result.Diagnostics, ReasonCodeNameCanonicalized, contracts.DiagnosticDispositionObservation) || !hasDiagnostic(result.Diagnostics, ReasonCodeUnknownItemID, contracts.DiagnosticDispositionAdvisory) {
		t.Fatalf("diagnostics = %#v", result.Diagnostics)
	}
}

func TestNormalizeRequiresRegistryAndRegistersSlot(t *testing.T) {
	normalizer, err := New(Options{})
	if err != nil {
		t.Fatal(err)
	}
	_, err = normalizer.Normalize(context.Background(), contracts.TypedNormalizeRequest[dnd.ItemOccurrenceList]{MergeOutput: contracts.MergeArtifact[dnd.ItemOccurrenceList]{Value: dnd.ItemOccurrenceList{Occurrences: []dnd.ItemOccurrence{}}}})
	if err == nil {
		t.Fatal("Normalize() error = nil")
	}
	if len(ModuleSpec().ReferenceSlots) != 1 || !ModuleSpec().ReferenceSlots[0].Required {
		t.Fatalf("ModuleSpec() = %#v", ModuleSpec())
	}
	registry := pipeline.NewNormalizerRegistry()
	if err := Register(registry); err != nil {
		t.Fatal(err)
	}
}

func hasDiagnostic(diagnostics []contracts.ProducerDiagnostic, reason string, disposition contracts.DiagnosticDisposition) bool {
	for _, diagnostic := range diagnostics {
		if diagnostic.ReasonCode == reason && diagnostic.Disposition == disposition {
			return true
		}
	}
	return false
}
