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"
	thoughttypes "git.warky.dev/wdevs/amcs/internal/types"
)

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

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

type ContextItem struct {
	ID         string                       `json:"id"`
	Content    string                       `json:"content"`
	Metadata   thoughttypes.ThoughtMetadata `json:"metadata"`
	Similarity float64                      `json:"similarity,omitempty"`
	Source     string                       `json:"source"`
}

type ProjectContextOutput struct {
	Project thoughttypes.Project `json:"project"`
	Context string               `json:"context"`
	Items   []ContextItem        `json:"items"`
}

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

func (t *ContextTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in ProjectContextInput) (*mcp.CallToolResult, ProjectContextOutput, error) {
	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, true)
	if err != nil {
		return nil, ProjectContextOutput{}, err
	}

	limit := normalizeLimit(in.Limit, t.search)
	recent, err := t.store.RecentThoughts(ctx, &project.ID, limit, 0)
	if err != nil {
		return nil, ProjectContextOutput{}, err
	}

	items := make([]ContextItem, 0, limit*2)
	seen := map[string]struct{}{}
	for _, thought := range recent {
		key := thought.ID.String()
		seen[key] = struct{}{}
		items = append(items, ContextItem{
			ID:       key,
			Content:  thought.Content,
			Metadata: thought.Metadata,
			Source:   "recent",
		})
	}

	query := strings.TrimSpace(in.Query)
	if query != "" {
		semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, t.search.DefaultThreshold, &project.ID, nil)
		if err != nil {
			return nil, ProjectContextOutput{}, err
		}
		for _, result := range semantic {
			key := result.ID.String()
			if _, ok := seen[key]; ok {
				continue
			}
			seen[key] = struct{}{}
			items = append(items, ContextItem{
				ID:         key,
				Content:    result.Content,
				Metadata:   result.Metadata,
				Similarity: result.Similarity,
				Source:     "semantic",
			})
		}
	}

	lines := make([]string, 0, len(items))
	for i, item := range items {
		lines = append(lines, thoughtContextLine(i, item.Content, item.Metadata, item.Similarity))
	}
	contextBlock := formatContextBlock(fmt.Sprintf("Project context for %s", project.Name), lines)
	_ = t.store.TouchProject(ctx, project.ID)

	return nil, ProjectContextOutput{
		Project: *project,
		Context: contextBlock,
		Items:   items,
	}, nil
}
