package conversation

import (
	"context"
	"sort"
	"sync"
	"time"
)

// MemoryRepo bellek içi Repository uygulamasıdır. MongoDB yokken geliştirme ve testlerde kullanılır;
// süreç kapanınca veri kaybolur. Eşzamanlı kullanıma güvenlidir.
type MemoryRepo struct {
	mu    sync.RWMutex
	convs map[string]Conversation
	msgs  map[string][]Message
	now   func() time.Time
}

var _ Repository = (*MemoryRepo)(nil)

func NewMemoryRepo() *MemoryRepo {
	return &MemoryRepo{
		convs: make(map[string]Conversation),
		msgs:  make(map[string][]Message),
		now:   func() time.Time { return time.Now().UTC() },
	}
}

func (r *MemoryRepo) CreateConversation(_ context.Context, c *Conversation) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	cp := *c
	cp.Messages = nil
	r.convs[c.ID] = cp
	return nil
}

func (r *MemoryRepo) GetConversation(_ context.Context, id string) (*Conversation, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	c, ok := r.convs[id]
	if !ok {
		return nil, ErrNotFound
	}
	return &c, nil
}

func (r *MemoryRepo) ListConversations(_ context.Context, userID string, limit, offset int) ([]Conversation, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var out []Conversation
	for _, c := range r.convs {
		if c.UserID == userID {
			out = append(out, c)
		}
	}
	sort.Slice(out, func(i, j int) bool { return out[i].UpdatedAt.After(out[j].UpdatedAt) })
	if offset < 0 {
		offset = 0
	}
	if offset >= len(out) {
		return []Conversation{}, nil
	}
	out = out[offset:]
	if limit > 0 && limit < len(out) {
		out = out[:limit]
	}
	return out, nil
}

func (r *MemoryRepo) UpdateTitle(_ context.Context, id, title string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	c, ok := r.convs[id]
	if !ok {
		return ErrNotFound
	}
	c.Title = title
	c.UpdatedAt = r.now()
	r.convs[id] = c
	return nil
}

func (r *MemoryRepo) DeleteConversation(_ context.Context, id string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.convs[id]; !ok {
		return ErrNotFound
	}
	delete(r.convs, id)
	delete(r.msgs, id)
	return nil
}

func (r *MemoryRepo) AppendMessage(_ context.Context, m *Message) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	c, ok := r.convs[m.ConversationID]
	if !ok {
		return ErrNotFound
	}
	r.msgs[m.ConversationID] = append(r.msgs[m.ConversationID], *m)
	c.UpdatedAt = r.now()
	r.convs[c.ID] = c
	return nil
}

func (r *MemoryRepo) Messages(_ context.Context, conversationID string, limit int) ([]Message, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	if _, ok := r.convs[conversationID]; !ok {
		return nil, ErrNotFound
	}
	all := r.msgs[conversationID]
	if limit > 0 && limit < len(all) {
		all = all[len(all)-limit:]
	}
	return append([]Message(nil), all...), nil
}

// ResolveToolConfirm bekleyen kartı kapatır. Dizi KOPYALANARAK yazılır: Messages() daha önce
// aynı arka diziyi paylaşan kopyalar döndürmüş olabilir (veri yarışı olmasın).
func (r *MemoryRepo) ResolveToolConfirm(_ context.Context, conversationID, confirmID string, res ToolConfirmResolution) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.convs[conversationID]; !ok {
		return ErrNotFound
	}
	msgs := r.msgs[conversationID]
	for i := range msgs {
		for j, c := range msgs[i].ToolConfirms {
			if c.ConfirmID != confirmID || c.State != ConfirmPending {
				continue
			}
			cp := append([]ToolConfirm(nil), msgs[i].ToolConfirms...)
			now := r.now()
			cp[j].State, cp[j].Summary, cp[j].Values, cp[j].ResolvedAt = res.State, res.Summary, res.Values, &now
			msgs[i].ToolConfirms = cp
			return nil
		}
	}
	return ErrNotFound
}
