package agent

import (
	"io"
	"sync"
)

// kimiTranslatedOutput is a generation-scoped output queue. Producers may
// back-pressure while Process consumes translated events, but abort always
// releases them before teardown waits for the pipe-draining goroutine.
type kimiTranslatedOutput struct {
	mu sync.Mutex

	lines   chan string
	done    chan struct{}
	idle    chan struct{}
	aborted bool
	active  int

	abortOnce       sync.Once
	closeOnce       sync.Once
	writerCloseOnce sync.Once
	writerCloser    io.Closer
}

func newKimiTranslatedOutput(capacity int) *kimiTranslatedOutput {
	if capacity <= 0 {
		capacity = 1
	}
	idle := make(chan struct{})
	close(idle)
	return &kimiTranslatedOutput{
		lines: make(chan string, capacity),
		done:  make(chan struct{}),
		idle:  idle,
	}
}

func (output *kimiTranslatedOutput) attachWriter(writer io.Closer) {
	if output == nil || writer == nil {
		return
	}
	output.mu.Lock()
	output.writerCloser = writer
	aborted := output.aborted
	output.mu.Unlock()
	if aborted {
		output.closeWriter()
	}
}

func (output *kimiTranslatedOutput) closeWriter() {
	if output == nil {
		return
	}
	output.mu.Lock()
	writer := output.writerCloser
	output.mu.Unlock()
	if writer == nil {
		return
	}
	output.writerCloseOnce.Do(func() {
		_ = writer.Close()
	})
}

func (output *kimiTranslatedOutput) emit(line string) bool {
	if output == nil {
		return false
	}
	output.mu.Lock()
	if output.aborted {
		output.mu.Unlock()
		return false
	}
	if output.active == 0 {
		output.idle = make(chan struct{})
	}
	output.active++
	output.mu.Unlock()
	defer func() {
		output.mu.Lock()
		output.active--
		if output.active == 0 {
			close(output.idle)
		}
		output.mu.Unlock()
	}()

	select {
	case output.lines <- line:
		return true
	case <-output.done:
		return false
	}
}

func (output *kimiTranslatedOutput) abort() {
	if output == nil {
		return
	}
	output.abortOnce.Do(func() {
		output.mu.Lock()
		output.aborted = true
		close(output.done)
		output.mu.Unlock()
	})
	output.closeWriter()
}

func (output *kimiTranslatedOutput) close() {
	if output == nil {
		return
	}
	output.abort()
	output.closeOnce.Do(func() {
		output.mu.Lock()
		idle := output.idle
		output.mu.Unlock()
		<-idle
		close(output.lines)
	})
}
