package execution

import (
	"context"
	"sync"
	"testing"
)

type conformanceProvider struct {
	mu    sync.Mutex
	specs []ExecutionSpec
}

func (p *conformanceProvider) Name() string { return "fixture" }

func (p *conformanceProvider) Capabilities(context.Context) (Capabilities, error) {
	return Capabilities{AdapterName: p.Name(), Targets: []string{"fixture"}, MaxConcurrency: 1}, nil
}

func (p *conformanceProvider) Execute(ctx context.Context, spec ExecutionSpec, sink EventSink) error {
	p.mu.Lock()
	p.specs = append(p.specs, spec)
	p.mu.Unlock()
	for _, event := range []RuntimeEvent{
		{RunID: spec.RunID, Type: EventTypeStart},
		{RunID: spec.RunID, Type: EventTypeDelta, Delta: spec.SessionID},
		{RunID: spec.RunID, Type: EventTypeComplete},
		{RunID: spec.RunID, Type: EventTypeComplete, Message: "duplicate"},
	} {
		if err := sink.Emit(ctx, event); err != nil {
			return err
		}
	}
	return nil
}

type nodeHostFixture struct{}

func (nodeHostFixture) run(ctx context.Context, provider Provider, spec ExecutionSpec, sink EventSink) error {
	return provider.Execute(ctx, spec, NewTerminalEmitter(sink))
}

func TestProviderHostConformance(t *testing.T) {
	provider := &conformanceProvider{}
	tests := []struct {
		name string
		run  func(context.Context, Provider, ExecutionSpec, EventSink) error
	}{
		{name: "first-run", run: nodeHostFixture{}.run},
		{name: "same-correlation-id", run: nodeHostFixture{}.run},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			recorder := &eventRecorder{}
			spec := ExecutionSpec{
				RunID:     "run-" + test.name,
				Adapter:   provider.Name(),
				Target:    "fixture",
				SessionID: "shared-correlation",
			}
			if err := test.run(context.Background(), provider, spec, recorder); err != nil {
				t.Fatalf("run() error = %v", err)
			}
			if len(recorder.events) != 3 || recorder.events[2].Type != EventTypeComplete {
				t.Fatalf("events = %#v", recorder.events)
			}
		})
	}

	provider.mu.Lock()
	defer provider.mu.Unlock()
	if len(provider.specs) != 2 {
		t.Fatalf("executed specs = %d, want 2", len(provider.specs))
	}
	if provider.specs[0].RunID == provider.specs[1].RunID ||
		provider.specs[0].SessionID != provider.specs[1].SessionID {
		t.Fatalf("correlation-only specs = %#v", provider.specs)
	}
}
