// Package event process-içi bir olay yolu (event bus) soyutlaması sağlar.
// Bus arayüzü, ileride NATS / Redis Streams / Kafka gibi bir uygulamayla
// değiştirilebilecek şekilde tasarlanmıştır; tüketiciler yalnızca arayüzü bilir.
package event

import (
	"context"
	"log/slog"
	"sync"
	"time"
)

type Type string

const (
	UserMessageReceived     Type = "UserMessageReceived"
	AgentStarted            Type = "AgentStarted"
	AgentCompleted          Type = "AgentCompleted"
	AgentFailed             Type = "AgentFailed"
	ConversationInterrupted Type = "ConversationInterrupted"

	// Konuşma yaşam döngüsü olayları (REST katmanı yayınlar).
	ConversationCreated Type = "ConversationCreated"
	ConversationDeleted Type = "ConversationDeleted"
	ConversationRenamed Type = "ConversationRenamed"

	// Ayarlar değiştiğinde yayınlanır (UI ve ajan yeniden okur).
	SettingsChanged Type = "SettingsChanged"

	// Skill kaydı yaşam döngüsü (Data: id, name). Retrieval indeksi bunlara abone olabilir.
	SkillCreated Type = "SkillCreated"
	SkillUpdated Type = "SkillUpdated"
	SkillDeleted Type = "SkillDeleted"

	// Skill yürütme (Data: id, name, tool). Araç katmanı bunları izleyerek
	// "hangi skill hangi tool'u çalıştırdı" sorusunu yanıtlar.
	SkillExecuted      Type = "SkillExecuted"
	SkillExecuteFailed Type = "SkillExecuteFailed"

	// Tool Registry yaşam döngüsü (Data: id, name) ve yürütme (Data: tool, durationMs, attempts).
	ToolRegistered Type = "ToolRegistered"
	ToolUpdated    Type = "ToolUpdated"
	ToolDeleted    Type = "ToolDeleted"
	ToolExecuted   Type = "ToolExecuted"

	// Bilgi tabanı (Faz 8) belge yaşam döngüsü (Data: id, title, chunks / id).
	KnowledgeDocumentAdded   Type = "KnowledgeDocumentAdded"
	KnowledgeDocumentDeleted Type = "KnowledgeDocumentDeleted"

	// Bellek (P10) yaşam döngüsü (Data: id, kind, origin / id). İçerik olaya KONMAZ (kişisel veri).
	MemoryAdded   Type = "MemoryAdded"
	MemoryUpdated Type = "MemoryUpdated"
	MemoryDeleted Type = "MemoryDeleted"

	// Denetim kaydı: kullanıcı/ajan işlemleri. Metadata yazılmaz (dosyaya yazılır).
	AuditRecorded Type = "AuditRecorded"
)

type Event struct {
	Type           Type
	At             time.Time
	UserID         string
	ConversationID string
	Data           map[string]any
}

type Handler func(ctx context.Context, e Event)

// Bus yayıncı/abone sözleşmesidir.
type Bus interface {
	Publish(ctx context.Context, e Event)
	// Subscribe belirli bir olay türüne abone olur; dönen fonksiyon aboneliği kaldırır.
	Subscribe(t Type, h Handler) (unsubscribe func())
	// SubscribeAll tüm olaylara abone olur.
	SubscribeAll(h Handler) (unsubscribe func())
	Close()
}

// MemoryBus tek süreçlik, tamponlu ve asenkron dağıtım yapan Bus uygulamasıdır.
// Yayıncıyı asla bloklamaz: tampon doluysa olay düşer ve uyarı loglanır.
type MemoryBus struct {
	log *slog.Logger

	mu       sync.RWMutex
	closed   bool
	next     int
	byType   map[Type]map[int]Handler
	all      map[int]Handler
	queue    chan Event
	finished chan struct{}
}

func NewMemoryBus(log *slog.Logger, buffer int) *MemoryBus {
	if buffer < 1 {
		buffer = 256
	}
	b := &MemoryBus{
		log:      log,
		byType:   make(map[Type]map[int]Handler),
		all:      make(map[int]Handler),
		queue:    make(chan Event, buffer),
		finished: make(chan struct{}),
	}
	go b.dispatch()
	return b
}

func (b *MemoryBus) Publish(_ context.Context, e Event) {
	if e.At.IsZero() {
		e.At = time.Now().UTC()
	}
	b.mu.RLock()
	defer b.mu.RUnlock()
	if b.closed {
		return
	}
	select {
	case b.queue <- e:
	default:
		b.log.Warn("event dropped: queue full", "type", e.Type)
	}
}

func (b *MemoryBus) Subscribe(t Type, h Handler) func() {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.next++
	n := b.next
	if b.byType[t] == nil {
		b.byType[t] = make(map[int]Handler)
	}
	b.byType[t][n] = h
	return func() {
		b.mu.Lock()
		defer b.mu.Unlock()
		delete(b.byType[t], n)
	}
}

func (b *MemoryBus) SubscribeAll(h Handler) func() {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.next++
	n := b.next
	b.all[n] = h
	return func() {
		b.mu.Lock()
		defer b.mu.Unlock()
		delete(b.all, n)
	}
}

// Close kuyruğu boşaltır ve dağıtıcıyı durdurur.
func (b *MemoryBus) Close() {
	b.mu.Lock()
	if b.closed {
		b.mu.Unlock()
		return
	}
	b.closed = true
	close(b.queue)
	b.mu.Unlock()
	<-b.finished
}

func (b *MemoryBus) dispatch() {
	defer close(b.finished)
	for e := range b.queue {
		b.mu.RLock()
		handlers := make([]Handler, 0, len(b.all)+len(b.byType[e.Type]))
		for _, h := range b.all {
			handlers = append(handlers, h)
		}
		for _, h := range b.byType[e.Type] {
			handlers = append(handlers, h)
		}
		b.mu.RUnlock()

		for _, h := range handlers {
			b.safeCall(h, e)
		}
	}
}

func (b *MemoryBus) safeCall(h Handler, e Event) {
	defer func() {
		if r := recover(); r != nil {
			b.log.Error("event handler panic", "type", e.Type, "panic", r)
		}
	}()
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	h(ctx, e)
}
