package devagents

import (
	"context"
	"fmt"
	"log/slog"
	"path/filepath"

	coretypes "github.com/enmanuel/agents/pkg/llm"
	"github.com/enmanuel/agents/pkg/memory"
	shellmem "github.com/enmanuel/agents/shell/memory"
)

// ClearWindow resets the conversation window for a room and deletes persisted
// messages from SQLite so the agent starts fresh. Implements toolmemory.WindowClearer.
func (a *Agent) ClearWindow(roomID string) {
	a.windowsMu.Lock()
	a.windows[roomID] = memory.NewWindow(a.windowSize)
	a.windowsMu.Unlock()

	if a.memStore != nil {
		if err := a.memStore.DeleteMessages(
			context.Background(), a.cfg.Agent.ID, &roomID,
		); err != nil {
			a.logger.Warn("failed to delete persisted messages on clear", "room", roomID, "err", err)
		}
	}
}

// ensureWindowLoaded loads the conversation window from SQLite on first access for a room.
func (a *Agent) ensureWindowLoaded(ctx context.Context, roomID string) {
	a.windowsMu.Lock()
	defer a.windowsMu.Unlock()
	if _, ok := a.windows[roomID]; ok {
		return
	}
	w := memory.NewWindow(a.windowSize)
	if a.memStore != nil {
		msgs, err := a.memStore.LoadMessages(ctx, a.cfg.Agent.ID, roomID, a.windowSize)
		if err != nil {
			a.logger.Warn("failed to load message history", "room", roomID, "err", err)
		} else {
			for _, m := range msgs {
				w = w.Append(coretypes.Message{Role: m.Role, Content: m.Content})
			}
			if len(msgs) > 0 {
				a.logger.Debug("loaded message history", "room", roomID, "count", len(msgs))
			}
		}
	}
	a.windows[roomID] = w
}

// appendToWindow adds a message to the in-memory conversation window.
func (a *Agent) appendToWindow(roomID string, msg coretypes.Message) {
	a.windowsMu.Lock()
	defer a.windowsMu.Unlock()
	w, ok := a.windows[roomID]
	if !ok {
		w = memory.NewWindow(a.windowSize)
	}
	a.windows[roomID] = w.Append(msg)
}

// getWindowMessages returns a copy of the conversation window for a room.
func (a *Agent) getWindowMessages(roomID string) []coretypes.Message {
	a.windowsMu.RLock()
	defer a.windowsMu.RUnlock()
	w, ok := a.windows[roomID]
	if !ok {
		return nil
	}
	return w.ToLLMMessages()
}

// persistMessage saves a message to the SQLite store (no-op if store is nil).
func (a *Agent) persistMessage(ctx context.Context, roomID string, role coretypes.Role, content string) {
	if a.memStore == nil {
		return
	}
	if err := a.memStore.SaveMessage(ctx, memory.HistoryMessage{
		AgentID: a.cfg.Agent.ID,
		RoomID:  roomID,
		Role:    role,
		Content: content,
	}); err != nil {
		a.logger.Warn("failed to persist message", "room", roomID, "err", err)
	}
}

// memoryInit holds the results of memory subsystem initialization.
type memoryInit struct {
	store      memory.Store
	windowSize int
}

// initMemoryStore creates the memory store and resolves window size from config.
// Returns a zero-value memoryInit if memory is disabled.
func initMemoryStore(enabled bool, windowSizeCfg int, dbPathCfg string, dataBase string, logger *slog.Logger) (memoryInit, error) {
	if !enabled {
		return memoryInit{windowSize: defaultWindowSize}, nil
	}

	windowSize := windowSizeCfg
	if windowSize <= 0 {
		windowSize = defaultWindowSize
	}

	dbPath := dbPathCfg
	if dbPath == "" {
		dbPath = filepath.Join(dataBase, "memory.db")
	}
	store, err := shellmem.New(dbPath, logger)
	if err != nil {
		return memoryInit{}, fmt.Errorf("memory store: %w", err)
	}
	logger.Info("memory enabled", "window_size", windowSize, "db", dbPath)
	return memoryInit{store: store, windowSize: windowSize}, nil
}
