package execution

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

type eventRecorder struct {
	events []RuntimeEvent
}

func (r *eventRecorder) Emit(_ context.Context, event RuntimeEvent) error {
	r.events = append(r.events, event)
	return nil
}

func TestTerminalEmitterForwardsExactlyOneTerminal(t *testing.T) {
	recorder := &eventRecorder{}
	emitter := NewTerminalEmitter(recorder)

	events := []RuntimeEvent{
		{RunID: "run-1", Type: EventTypeStart},
		{RunID: "run-1", Type: EventTypeDelta, Delta: "ok"},
		{RunID: "run-1", Type: EventTypeComplete},
		{RunID: "run-1", Type: EventTypeError, Error: "late"},
		{RunID: "run-1", Type: EventTypeDelta, Delta: "late"},
	}
	for _, event := range events {
		if err := emitter.Emit(context.Background(), event); err != nil {
			t.Fatalf("Emit() error = %v", err)
		}
	}

	if !emitter.TerminalObserved() {
		t.Fatal("terminal was not observed")
	}
	if len(recorder.events) != 3 {
		t.Fatalf("event count = %d, want 3: %#v", len(recorder.events), recorder.events)
	}
	if recorder.events[2].Type != EventTypeComplete {
		t.Fatalf("terminal = %q, want %q", recorder.events[2].Type, EventTypeComplete)
	}
}

type blockingSink struct {
	mu           sync.Mutex
	events       []RuntimeEvent
	firstEntered chan struct{}
	releaseFirst chan struct{}
}

func (s *blockingSink) Emit(_ context.Context, event RuntimeEvent) error {
	s.mu.Lock()
	first := len(s.events) == 0
	s.events = append(s.events, event)
	s.mu.Unlock()

	if first {
		close(s.firstEntered)
		<-s.releaseFirst
	}
	return nil
}

func TestTerminalEmitterPreservesConcurrentAcceptedOrder(t *testing.T) {
	sink := &blockingSink{
		firstEntered: make(chan struct{}),
		releaseFirst: make(chan struct{}),
	}
	emitter := NewTerminalEmitter(sink)

	deltaDone := make(chan error, 1)
	go func() {
		deltaDone <- emitter.Emit(context.Background(), RuntimeEvent{RunID: "run-1", Type: EventTypeDelta, Delta: "first"})
	}()

	<-sink.firstEntered // Delta has entered sink.Emit and is blocked.

	completeDone := make(chan error, 1)
	go func() {
		completeDone <- emitter.Emit(context.Background(), RuntimeEvent{RunID: "run-1", Type: EventTypeComplete})
	}()

	close(sink.releaseFirst)

	if err := <-deltaDone; err != nil {
		t.Fatalf("delta Emit failed: %v", err)
	}
	if err := <-completeDone; err != nil {
		t.Fatalf("complete Emit failed: %v", err)
	}

	sink.mu.Lock()
	defer sink.mu.Unlock()
	if len(sink.events) != 2 {
		t.Fatalf("events count = %d, want 2: %#v", len(sink.events), sink.events)
	}
	if sink.events[0].Type != EventTypeDelta || sink.events[1].Type != EventTypeComplete {
		t.Fatalf("events order = [%v, %v], want [EventTypeDelta, EventTypeComplete]", sink.events[0].Type, sink.events[1].Type)
	}
}
