package llm

import (
	"sync"
	"time"
)

// CallRecord records a single LLM API call.
type CallRecord struct {
	Time             time.Time     `json:"time"`
	Model            string        `json:"model"`
	Duration         time.Duration `json:"duration_ms"`
	PromptTokens     int           `json:"prompt_tokens"`
	CompletionTokens int           `json:"completion_tokens"`
	TotalTokens      int           `json:"total_tokens"`
	Success          bool          `json:"success"`
	Error            string        `json:"error,omitempty"`
}

// CallLogger is a thread-safe ring buffer for LLM call records.
type CallLogger struct {
	mu       sync.RWMutex
	records  []CallRecord
	capacity int
	head     int
	size     int
}

var globalCallLogger = &CallLogger{capacity: 500}

// LogCall records an LLM call. Safe for concurrent use.
func LogCall(r CallRecord) {
	globalCallLogger.log(r)
}

// GetCalls returns recent call records, newest first.
func GetCalls(limit int) []CallRecord {
	return globalCallLogger.get(limit)
}

func (cl *CallLogger) log(r CallRecord) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	if cl.records == nil {
		cl.records = make([]CallRecord, cl.capacity)
	}

	r.Time = time.Now()
	cl.records[cl.head] = r
	cl.head = (cl.head + 1) % cl.capacity
	if cl.size < cl.capacity {
		cl.size++
	}

	broadcastCall(r)
}

func (cl *CallLogger) get(limit int) []CallRecord {
	cl.mu.RLock()
	defer cl.mu.RUnlock()

	if limit <= 0 || limit > cl.size {
		limit = cl.size
	}

	result := make([]CallRecord, limit)
	for i := 0; i < limit; i++ {
		idx := (cl.head - 1 - i) % cl.capacity
		if idx < 0 {
			idx += cl.capacity
		}
		result[i] = cl.records[idx]
	}
	return result
}

// --- SSE subscriber system ---

type callSubscriber struct {
	ch   chan CallRecord
	done chan struct{}
}

var (
	callSubscribers   []*callSubscriber
	callSubscribersMu sync.RWMutex
)

// SubscribeCalls returns a channel that receives new CallRecords and a done channel.
func SubscribeCalls() (<-chan CallRecord, <-chan struct{}) {
	ch := make(chan CallRecord, 20)
	done := make(chan struct{})
	callSubscribersMu.Lock()
	callSubscribers = append(callSubscribers, &callSubscriber{ch: ch, done: done})
	callSubscribersMu.Unlock()
	return ch, done
}

// UnsubscribeCalls removes a subscriber. Safe to call multiple times.
func UnsubscribeCalls(ch <-chan CallRecord) {
	callSubscribersMu.Lock()
	defer callSubscribersMu.Unlock()
	for i, s := range callSubscribers {
		if s.ch == ch {
			close(s.done)
			callSubscribers = append(callSubscribers[:i], callSubscribers[i+1:]...)
			return
		}
	}
}

func broadcastCall(r CallRecord) {
	callSubscribersMu.RLock()
	defer callSubscribersMu.RUnlock()
	for _, s := range callSubscribers {
		select {
		case s.ch <- r:
		default:
		}
	}
}
