package main

import (
	"bytes"
	"context"
	"encoding/json"
	"os"
	"os/exec"

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

type runArgs struct {
	ID   string   `json:"id"`
	Args []string `json:"args,omitempty"`
}

func runTool() mcp.Tool {
	return mcp.NewTool("fn_run",
		mcp.WithDescription("Execute a registry function/pipeline via `fn run <id> [args...]`. Dispatches by language: Go (go run/test), Python (.venv), Bash, TypeScript (tsx). Mutating side-effects possible. Off by default — server must be launched with --enable-run."),
		mcp.WithString("id",
			mcp.Required(),
			mcp.Description("Registry ID or function name."),
		),
		mcp.WithArray("args",
			mcp.Description("Positional args appended to fn run."),
			mcp.Items(map[string]any{"type": "string"}),
		),
	)
}

func (d *deps) handleRun(ctx context.Context, _ mcp.CallToolRequest, args runArgs) (*mcp.CallToolResult, error) {
	if args.ID == "" {
		return mcp.NewToolResultError("id is required"), nil
	}
	bin := d.fnBin()
	cmdArgs := append([]string{"run", args.ID}, args.Args...)
	cmd := exec.CommandContext(ctx, bin, cmdArgs...)
	cmd.Dir = d.root
	cmd.Env = os.Environ()

	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	runErr := cmd.Run()

	exit := 0
	if cmd.ProcessState != nil {
		exit = cmd.ProcessState.ExitCode()
	}

	out := map[string]any{
		"id":        args.ID,
		"args":      args.Args,
		"exit_code": exit,
		"stdout":    truncate(stdout.String(), 100_000),
		"stderr":    truncate(stderr.String(), 100_000),
	}
	if runErr != nil && exit == 0 {
		out["error"] = runErr.Error()
	}

	b, _ := json.MarshalIndent(out, "", "  ")
	return mcp.NewToolResultText(string(b)), nil
}

func (d *deps) fnBin() string {
	if v := os.Getenv("FN_BIN"); v != "" {
		return v
	}
	candidate := d.root + "/fn"
	if _, err := os.Stat(candidate); err == nil {
		return candidate
	}
	if path, err := exec.LookPath("fn"); err == nil {
		return path
	}
	return "fn"
}
