package cli

import (
	"context"
	"errors"
	"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"
)

const invalidEnemyEventExtractorKey = "test/dnd/invalid-enemy-events"

func TestAssembledEnemyEventLaneRejectsInvalidFinalArtifactDespiteValidatorOverrides(t *testing.T) {
	components := productionTestComponents(t)
	if err := pipeline.RegisterExtractor[dnd.EnemyEventList](components.registries.Extractors, pipeline.ModuleSpec{
		Key:            invalidEnemyEventExtractorKey,
		Stage:          pipeline.StageExtract,
		ExecutionClass: contracts.ExecutionClassDeterministic,
		Requires:       []string{"chunks", "source.transcript"},
		Provides:       []string{"dnd.enemy_events"},
		ArtifactKind:   dnd.EnemyEventListKind,
	}, func() (contracts.Extractor[dnd.EnemyEventList], error) {
		return invalidEnemyEventExtractor{}, nil
	}); err != nil {
		t.Fatalf("register extractor: %v", err)
	}

	accept := pipeline.ValidatorOverride{Set: true, Validators: []pipeline.ModuleBinding{pipeline.Binding("generic/always_accept")}}
	resolved, err := pipeline.ResolvePipeline(pipeline.PipelineProfile{
		ID:    "assembled-invalid-enemy-events",
		Input: pipeline.Binding("seriatim"),
		Chunk: pipeline.ModuleBinding{Module: "generic", Options: map[string]any{"max_units": 1}},
		Artifacts: map[string]pipeline.ArtifactLaneProfile{
			"enemy-events": {
				Extract:   pipeline.ModuleBinding{Module: invalidEnemyEventExtractorKey, Validators: accept},
				Normalize: pipeline.ModuleBinding{Module: pipeline.DefaultNormalizeModule, Validators: accept},
			},
		},
		Output: pipeline.Binding("json"),
	}, pipeline.ResolveOptions{}, catalogFromRegistries(components.registries))
	if err != nil {
		t.Fatalf("ResolvePipeline() error = %v", err)
	}

	prepared, err := pipeline.Prepare(resolved, components.registries, pipeline.ModuleDependencies{})
	if err != nil {
		t.Fatalf("Prepare() error = %v", err)
	}
	_, err = pipeline.New().Run(context.Background(), pipeline.RunInput{
		Prepared:       prepared,
		RawInput:       readRepositoryFile(t, "examples", "seriatim-minimal-transcript.json"),
		ChunkCacheMode: pipeline.ChunkCacheBypass,
	})
	if err == nil || !strings.Contains(err.Error(), "serialize accepted extract output") || !strings.Contains(err.Error(), "must not exceed") {
		t.Fatalf("Run() error = %v, want final durable range rejection", err)
	}
}

type invalidEnemyEventExtractor struct{}

func (invalidEnemyEventExtractor) Key() string { return invalidEnemyEventExtractorKey }

func (invalidEnemyEventExtractor) ReferenceSlots() []contracts.ReferenceSlot { return nil }

func (invalidEnemyEventExtractor) Extract(ctx context.Context, req contracts.TypedExtractionRequest) (contracts.TypedExtractionResult[dnd.EnemyEventList], error) {
	if err := ctx.Err(); err != nil {
		return contracts.TypedExtractionResult[dnd.EnemyEventList]{}, err
	}
	if req.Source == nil {
		return contracts.TypedExtractionResult[dnd.EnemyEventList]{}, errors.New("assembled extractor requires source")
	}
	return contracts.TypedExtractionResult[dnd.EnemyEventList]{Value: dnd.EnemyEventList{Events: []dnd.EnemyEvent{{
		Name:       "Ashfang",
		Kind:       dnd.EnemyEventKindEngaged,
		SourceRefs: []source.SourceRef{{SourceID: req.Source.ID, StartUnitID: 2, EndUnitID: 1}},
	}}}}, nil
}
