package api

import "sync"

// LogBuffer is a thread-safe ring buffer that stores the most recent N log lines.
// It supports incremental reads via LinesSince and tracks a runID that increments
// whenever the buffer is reset or cleared so clients can detect log history resets.
type LogBuffer struct {
	mu    sync.RWMutex
	lines []string
	cap   int
	total int // total lines ever appended in current run
	runID int
}

// NewLogBuffer creates a LogBuffer with the given capacity.
func NewLogBuffer(capacity int) *LogBuffer {
	return &LogBuffer{
		lines: make([]string, 0, capacity),
		cap:   capacity,
	}
}

// Append adds a line to the buffer. If the buffer is full, the oldest line is evicted.
func (b *LogBuffer) Append(line string) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if len(b.lines) < b.cap {
		b.lines = append(b.lines, line)
	} else {
		b.lines[b.total%b.cap] = line
	}

	b.total++
}

// Reset clears the buffer and increments the runID. Call this when starting a new gateway process.
func (b *LogBuffer) Reset() {
	b.mu.Lock()
	defer b.mu.Unlock()

	b.lines = b.lines[:0]
	b.total = 0
	b.runID++
}

// Clear removes all buffered lines and increments the runID so clients treat
// subsequent reads as a new log stream.
func (b *LogBuffer) Clear() {
	b.Reset()
}

// LinesSince returns lines appended after the given offset, the current total count, and the runID.
// If offset >= total, no lines are returned. If offset is too old (evicted), all buffered lines are returned.
func (b *LogBuffer) LinesSince(offset int) (lines []string, total int, runID int) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	total = b.total
	runID = b.runID

	if offset >= b.total {
		return nil, total, runID
	}

	buffered := len(b.lines)

	// How many new lines since offset
	newCount := b.total - offset
	if newCount > buffered {
		newCount = buffered
	}

	result := make([]string, newCount)

	if b.total <= b.cap {
		// Buffer hasn't wrapped yet — simple slice
		copy(result, b.lines[buffered-newCount:])
	} else {
		// Buffer has wrapped — read from ring
		start := (b.total - newCount) % b.cap
		for i := range newCount {
			result[i] = b.lines[(start+i)%b.cap]
		}
	}

	return result, total, runID
}

// RunID returns the current run identifier.
func (b *LogBuffer) RunID() int {
	b.mu.RLock()
	defer b.mu.RUnlock()

	return b.runID
}
