package tools

import (
	"context"
	"fmt"
	"strings"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"git.warky.dev/wdevs/amcs/internal/ai"
	"git.warky.dev/wdevs/amcs/internal/config"
	"git.warky.dev/wdevs/amcs/internal/session"
	"git.warky.dev/wdevs/amcs/internal/store"
)

type RecallTool struct {
	store      *store.DB
	embeddings *ai.EmbeddingRunner
	search     config.SearchConfig
	sessions   *session.ActiveProjects
}

type RecallInput struct {
	Query   string `json:"query" jsonschema:"semantic query for recalled context"`
	Project string `json:"project,omitempty" jsonschema:"optional project name or id; falls back to the active session project"`
	Limit   int    `json:"limit,omitempty" jsonschema:"maximum number of context items to return"`
}

type RecallOutput struct {
	Context string        `json:"context"`
	Items   []ContextItem `json:"items"`
}

func NewRecallTool(db *store.DB, embeddings *ai.EmbeddingRunner, search config.SearchConfig, sessions *session.ActiveProjects) *RecallTool {
	return &RecallTool{store: db, embeddings: embeddings, search: search, sessions: sessions}
}

func (t *RecallTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in RecallInput) (*mcp.CallToolResult, RecallOutput, error) {
	query := strings.TrimSpace(in.Query)
	if query == "" {
		return nil, RecallOutput{}, errRequiredField("query")
	}

	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, false)
	if err != nil {
		return nil, RecallOutput{}, err
	}

	limit := normalizeLimit(in.Limit, t.search)

	var projectID *int64
	if project != nil {
		projectID = &project.NumericID
	}

	semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, t.search.DefaultThreshold, projectID, nil)
	if err != nil {
		return nil, RecallOutput{}, err
	}
	recent, err := t.store.RecentThoughts(ctx, projectID, limit, 0)
	if err != nil {
		return nil, RecallOutput{}, err
	}

	items := make([]ContextItem, 0, limit*2)
	seen := map[string]struct{}{}
	for _, result := range semantic {
		key := fmt.Sprint(result.ID)
		seen[key] = struct{}{}
		items = append(items, ContextItem{
			ID:         key,
			Content:    result.Content,
			Metadata:   result.Metadata,
			Similarity: result.Similarity,
			Source:     "semantic",
		})
	}
	for _, thought := range recent {
		key := fmt.Sprint(thought.ID)
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		items = append(items, ContextItem{
			ID:       key,
			Content:  thought.Content,
			Metadata: thought.Metadata,
			Source:   "recent",
		})
	}

	lines := make([]string, 0, len(items))
	for i, item := range items {
		lines = append(lines, thoughtContextLine(i, item.Content, item.Metadata, item.Similarity))
	}
	header := "Recalled context"
	if project != nil {
		header = fmt.Sprintf("Recalled context for %s", project.Name)
		_ = t.store.TouchProject(ctx, project.NumericID)
	}

	return nil, RecallOutput{
		Context: formatContextBlock(header, lines),
		Items:   items,
	}, nil
}
