package execution

import (
	"context"
	"sync"
)

// IsTerminalEvent reports whether an event closes one runtime execution.
func IsTerminalEvent(eventType EventType) bool {
	switch eventType {
	case EventTypeComplete, EventTypeError, EventTypeCancelled:
		return true
	default:
		return false
	}
}

// TerminalEmitter enforces at-most-one terminal event while preserving the
// wrapped sink's ordering. Duplicate terminal and post-terminal events are
// ignored so a host cannot expose more than one terminal boundary.
type TerminalEmitter struct {
	sink EventSink

	emitMu   sync.Mutex
	mu       sync.Mutex
	terminal bool
}

// NewTerminalEmitter wraps sink with terminal exactly-once protection.
func NewTerminalEmitter(sink EventSink) *TerminalEmitter {
	return &TerminalEmitter{sink: sink}
}

// Emit forwards the event unless the terminal boundary was already observed.
func (e *TerminalEmitter) Emit(ctx context.Context, event RuntimeEvent) error {
	e.emitMu.Lock()
	defer e.emitMu.Unlock()

	e.mu.Lock()
	if e.terminal {
		e.mu.Unlock()
		return nil
	}
	if IsTerminalEvent(event.Type) {
		e.terminal = true
	}
	e.mu.Unlock()
	return e.sink.Emit(ctx, event)
}

// TerminalObserved reports whether a terminal event was accepted.
func (e *TerminalEmitter) TerminalObserved() bool {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.terminal
}
