package main

import (
	"context"
	"encoding/json"

	"github.com/mark3labs/mcp-go/mcp"

	"fn-registry/registry"
)

type searchArgs struct {
	Query  string `json:"query"`
	Kind   string `json:"kind,omitempty"`
	Lang   string `json:"lang,omitempty"`
	Domain string `json:"domain,omitempty"`
	Purity string `json:"purity,omitempty"`
	Limit  int    `json:"limit,omitempty"`
}

type searchHit struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Kind        string `json:"kind"`
	Lang        string `json:"lang"`
	Domain      string `json:"domain"`
	Purity      string `json:"purity"`
	Signature   string `json:"signature,omitempty"`
	Description string `json:"description"`
	Algebraic   string `json:"algebraic,omitempty"`
	Entity      string `json:"entity"` // "function" | "type"
}

func searchTool() mcp.Tool {
	return mcp.NewTool("fn_search",
		mcp.WithDescription("Search the registry (functions + types) via FTS5. Free-text query with optional filters. Returns up to `limit` hits ordered by name. Use this BEFORE writing new code to avoid duplicates."),
		mcp.WithString("query",
			mcp.Required(),
			mcp.Description("FTS5 expression or free text. Examples: 'slice', 'name:filter*', 'description:\"single-page\"', 'sqlite OR fts5'."),
		),
		mcp.WithString("kind",
			mcp.Description("Filter by kind: function, pipeline, component."),
			mcp.Enum("function", "pipeline", "component"),
		),
		mcp.WithString("lang",
			mcp.Description("Filter by language: go, py, bash, ts, cpp, ps."),
		),
		mcp.WithString("domain",
			mcp.Description("Filter by domain: core, infra, finance, datascience, cybersecurity, shell, tui, pipelines, browser, ..."),
		),
		mcp.WithString("purity",
			mcp.Description("Filter by purity (functions only)."),
			mcp.Enum("pure", "impure"),
		),
		mcp.WithNumber("limit",
			mcp.Description("Max hits returned (default 50)."),
			mcp.Min(1),
			mcp.Max(500),
		),
	)
}

func (d *deps) handleSearch(ctx context.Context, _ mcp.CallToolRequest, args searchArgs) (*mcp.CallToolResult, error) {
	limit := args.Limit
	if limit <= 0 {
		limit = 50
	}

	q := sanitizeFTS5(args.Query)

	fns, err := d.db.SearchFunctions(q, registry.Kind(args.Kind), registry.Purity(args.Purity), args.Lang, args.Domain)
	if err != nil {
		return mcp.NewToolResultError("search functions: " + err.Error()), nil
	}

	var hits []searchHit
	for _, f := range fns {
		hits = append(hits, searchHit{
			ID:          f.ID,
			Name:        f.Name,
			Kind:        string(f.Kind),
			Lang:        f.Lang,
			Domain:      f.Domain,
			Purity:      string(f.Purity),
			Signature:   f.Signature,
			Description: f.Description,
			Entity:      "function",
		})
		if len(hits) >= limit {
			break
		}
	}

	// Types: only when no kind filter (kind applies only to functions).
	if args.Kind == "" && len(hits) < limit {
		ts, err := d.db.SearchTypes(q, args.Lang, args.Domain)
		if err != nil {
			return mcp.NewToolResultError("search types: " + err.Error()), nil
		}
		for _, t := range ts {
			hits = append(hits, searchHit{
				ID:          t.ID,
				Name:        t.Name,
				Lang:        t.Lang,
				Domain:      t.Domain,
				Algebraic:   string(t.Algebraic),
				Description: t.Description,
				Entity:      "type",
			})
			if len(hits) >= limit {
				break
			}
		}
	}

	out := map[string]any{
		"query":   args.Query,
		"count":   len(hits),
		"limit":   limit,
		"results": hits,
	}
	b, _ := json.MarshalIndent(out, "", "  ")
	return mcp.NewToolResultText(string(b)), nil
}
