package memorytools

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/enmanuel/agents/pkg/memory"
	"github.com/enmanuel/agents/tools"
)

// MemoryStore is the subset of memory.Store needed by memory tools.
type MemoryStore interface {
	SaveFact(ctx context.Context, fact memory.Fact) error
	RecallFacts(ctx context.Context, agentID, subject string, key *string) ([]memory.Fact, error)
	DeleteFacts(ctx context.Context, agentID, subject string, key *string) error
}

// WindowClearer allows tools to clear the conversation window for a room.
type WindowClearer interface {
	ClearWindow(roomID string)
}

// RoomContext is a thread-safe holder for the current room ID.
// Set by the runtime before each event handling; read by memory_clear_context.
type RoomContext struct {
	mu     sync.RWMutex
	roomID string
}

// Set updates the current room ID.
func (rc *RoomContext) Set(roomID string) {
	rc.mu.Lock()
	rc.roomID = roomID
	rc.mu.Unlock()
}

// Get returns the current room ID.
func (rc *RoomContext) Get() string {
	rc.mu.RLock()
	defer rc.mu.RUnlock()
	return rc.roomID
}

// NewMemorySave creates a tool that saves a fact to long-term memory.
func NewMemorySave(agentID string, store MemoryStore) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "memory_save",
			Description: "Save a fact to long-term memory. Use this to remember important information about users, topics, or preferences.",
			Parameters: []tools.Param{
				{Name: "subject", Type: "string", Description: "The subject this fact is about (e.g. a username, a topic)", Required: true},
				{Name: "key", Type: "string", Description: "The fact key (e.g. 'favorite_language', 'timezone')", Required: true},
				{Name: "value", Type: "string", Description: "The fact value to store", Required: true},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			subject := tools.GetString(args, "subject")
			key := tools.GetString(args, "key")
			value := tools.GetString(args, "value")
			if subject == "" || key == "" || value == "" {
				return tools.Result{Err: fmt.Errorf("memory_save: subject, key, and value are required")}
			}
			err := store.SaveFact(ctx, memory.Fact{
				AgentID: agentID,
				Subject: subject,
				Key:     key,
				Value:   value,
			})
			if err != nil {
				return tools.Result{Err: fmt.Errorf("memory_save: %w", err)}
			}
			return tools.Result{Output: fmt.Sprintf("saved: %s.%s = %s", subject, key, value)}
		},
	}
}

// NewMemoryRecall creates a tool that retrieves facts from long-term memory.
func NewMemoryRecall(agentID string, store MemoryStore) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "memory_recall",
			Description: "Recall facts from long-term memory about a subject. Omit key to get all facts for the subject.",
			Parameters: []tools.Param{
				{Name: "subject", Type: "string", Description: "The subject to recall facts about", Required: true},
				{Name: "key", Type: "string", Description: "Optional specific fact key to recall", Required: false},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			subject := tools.GetString(args, "subject")
			if subject == "" {
				return tools.Result{Err: fmt.Errorf("memory_recall: subject is required")}
			}
			var keyPtr *string
			if k := tools.GetString(args, "key"); k != "" {
				keyPtr = &k
			}
			facts, err := store.RecallFacts(ctx, agentID, subject, keyPtr)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("memory_recall: %w", err)}
			}
			if len(facts) == 0 {
				return tools.Result{Output: fmt.Sprintf("no facts found for subject %q", subject)}
			}
			var sb strings.Builder
			for _, f := range facts {
				fmt.Fprintf(&sb, "%s.%s = %s\n", f.Subject, f.Key, f.Value)
			}
			return tools.Result{Output: sb.String()}
		},
	}
}

// NewMemoryForget creates a tool that deletes facts from long-term memory.
func NewMemoryForget(agentID string, store MemoryStore) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "memory_forget",
			Description: "Delete facts from long-term memory. Omit key to delete all facts for the subject.",
			Parameters: []tools.Param{
				{Name: "subject", Type: "string", Description: "The subject whose facts to delete", Required: true},
				{Name: "key", Type: "string", Description: "Optional specific fact key to delete; omit to delete all", Required: false},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			subject := tools.GetString(args, "subject")
			if subject == "" {
				return tools.Result{Err: fmt.Errorf("memory_forget: subject is required")}
			}
			var keyPtr *string
			if k := tools.GetString(args, "key"); k != "" {
				keyPtr = &k
			}
			err := store.DeleteFacts(ctx, agentID, subject, keyPtr)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("memory_forget: %w", err)}
			}
			if keyPtr != nil {
				return tools.Result{Output: fmt.Sprintf("forgot %s.%s", subject, *keyPtr)}
			}
			return tools.Result{Output: fmt.Sprintf("forgot all facts about %s", subject)}
		},
	}
}

// NewMemoryClearContext creates a tool that clears the conversation window.
func NewMemoryClearContext(clearer WindowClearer, roomCtx *RoomContext) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "memory_clear_context",
			Description: "Clear the conversation context window. Useful to start fresh. Omit room_id to clear the current room.",
			Parameters: []tools.Param{
				{Name: "room_id", Type: "string", Description: "Optional room ID to clear; defaults to current room", Required: false},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			roomID := tools.GetString(args, "room_id")
			if roomID == "" {
				roomID = roomCtx.Get()
			}
			if roomID == "" {
				return tools.Result{Err: fmt.Errorf("memory_clear_context: no room_id provided and no current room")}
			}
			clearer.ClearWindow(roomID)
			return tools.Result{Output: fmt.Sprintf("conversation context cleared for room %s", roomID)}
		},
	}
}

// NewMemorySummary creates a tool that saves an important summary to long-term memory.
func NewMemorySummary(agentID string, store MemoryStore) tools.Tool {
	return tools.Tool{
		Def: tools.Def{
			Name:        "memory_summary",
			Description: "Save an important summary or takeaway from the current conversation to long-term memory.",
			Parameters: []tools.Param{
				{Name: "text", Type: "string", Description: "The summary text to save", Required: true},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			text := tools.GetString(args, "text")
			if text == "" {
				return tools.Result{Err: fmt.Errorf("memory_summary: text is required")}
			}
			key := time.Now().UTC().Format("2006-01-02T15:04:05")
			err := store.SaveFact(ctx, memory.Fact{
				AgentID: agentID,
				Subject: "_summary",
				Key:     key,
				Value:   text,
			})
			if err != nil {
				return tools.Result{Err: fmt.Errorf("memory_summary: %w", err)}
			}
			return tools.Result{Output: "summary saved"}
		},
	}
}
