package mcp

import (
	"context"

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

	"github.com/yan1h/agent-coding-workflow/internal/codeindex"
)

// registerCodeIndexTools registers the pgvector code-search tools. When the
// CodeIndex service is not configured (no embedding endpoint), the tools are not
// registered at all so they don't appear in the agent's tool list.
func registerCodeIndexTools(srv *mcpsdk.Server, deps ServerDeps) {
	if deps.CodeIndex == nil {
		return
	}
	registerSearchCode(srv, deps)
	registerReindexWorkspace(srv, deps)
}

// ===== search_code =====

type searchCodeIn struct {
	WorkspaceID string `json:"workspace_id" jsonschema:"workspace UUID"`
	Query       string `json:"query" jsonschema:"natural-language or code query"`
	TopK        int    `json:"top_k,omitempty" jsonschema:"max results (capped by server)"`
	PathPrefix  string `json:"path_prefix,omitempty" jsonschema:"restrict to a worktree-relative path prefix"`
	Lang        string `json:"lang,omitempty" jsonschema:"restrict to a language label (e.g. go, typescript)"`
}

type codeSnippet struct {
	File      string  `json:"file"`
	StartLine int     `json:"start_line"`
	EndLine   int     `json:"end_line"`
	Snippet   string  `json:"snippet"`
	Score     float32 `json:"score"`
	CommitSHA string  `json:"commit_sha"`
}

type searchCodeOut struct {
	Results []codeSnippet `json:"results"`
}

func registerSearchCode(srv *mcpsdk.Server, deps ServerDeps) {
	mcpsdk.AddTool(srv,
		&mcpsdk.Tool{Name: "search_code", Description: "Semantic code search over the workspace's indexed files; returns ranked file:line snippets."},
		func(ctx context.Context, _ *mcpsdk.CallToolRequest, in searchCodeIn) (*mcpsdk.CallToolResult, searchCodeOut, error) {
			if err := CheckToolFromCtx(ctx, "search_code"); err != nil {
				return nil, searchCodeOut{}, err
			}
			c, err := deps.Caller.Resolve(ctx)
			if err != nil {
				return nil, searchCodeOut{}, err
			}
			w, _, err := scopedWorkspace(ctx, deps, c, in.WorkspaceID)
			if err != nil {
				return nil, searchCodeOut{}, err
			}
			snippets, err := deps.CodeIndex.Search(ctx, w.ID, codeindex.SearchQuery{
				Text: in.Query, TopK: in.TopK, PathPrefix: in.PathPrefix, Lang: in.Lang,
			})
			if err != nil {
				return nil, searchCodeOut{}, err
			}
			out := searchCodeOut{Results: make([]codeSnippet, 0, len(snippets))}
			for _, s := range snippets {
				out.Results = append(out.Results, codeSnippet{
					File: s.FilePath, StartLine: s.StartLine, EndLine: s.EndLine,
					Snippet: s.Content, Score: s.Score, CommitSHA: s.CommitSHA,
				})
			}
			return nil, out, nil
		})
}

// ===== reindex_workspace =====

type reindexWorkspaceIn struct {
	WorkspaceID string `json:"workspace_id" jsonschema:"workspace UUID"`
}

type reindexWorkspaceOut struct {
	OK    bool   `json:"ok"`
	RunID string `json:"run_id"`
}

func registerReindexWorkspace(srv *mcpsdk.Server, deps ServerDeps) {
	mcpsdk.AddTool(srv,
		&mcpsdk.Tool{Name: "reindex_workspace", Description: "Enqueue a fresh code index build at the workspace's current main HEAD."},
		func(ctx context.Context, _ *mcpsdk.CallToolRequest, in reindexWorkspaceIn) (*mcpsdk.CallToolResult, reindexWorkspaceOut, error) {
			if err := CheckToolFromCtx(ctx, "reindex_workspace"); err != nil {
				return nil, reindexWorkspaceOut{}, err
			}
			c, err := deps.Caller.Resolve(ctx)
			if err != nil {
				return nil, reindexWorkspaceOut{}, err
			}
			w, _, err := scopedWorkspace(ctx, deps, c, in.WorkspaceID)
			if err != nil {
				return nil, reindexWorkspaceOut{}, err
			}
			run, err := deps.CodeIndex.EnqueueBuild(ctx, w.ID, nil, "")
			if err != nil {
				return nil, reindexWorkspaceOut{}, err
			}
			return nil, reindexWorkspaceOut{OK: true, RunID: run.ID.String()}, nil
		})
}
