/**
 * InsertAroundSymbol — insert code adjacent to a named declaration.
 *
 * Backed by the native `insertAroundSymbol` API. Position semantics (per
 * `@singularity-forge/native/edit`):
 *   - "BeforeDecl"   — immediately before the declaration's start
 *   - "AfterDecl"    — immediately after the declaration's end
 *   - "AtBodyStart"  — first line inside the declaration's body block
 *   - "AtBodyEnd"    — last line inside the declaration's body block
 *
 * Common uses: adding a docstring/decorator before a function (BeforeDecl),
 * inserting a sibling declaration (AfterDecl), prepending an early-return
 * guard inside a function (AtBodyStart).
 */

import { type Static, Type } from "@sinclair/typebox";
import type { AgentTool } from "@singularity-forge/agent-core";
import { insertAroundSymbol } from "@singularity-forge/native/edit";
import { notifyFileChanged } from "../lsp/client.js";
import { resolveToCwd } from "./path-utils.js";

const insertAroundSymbolSchema = Type.Object({
	path: Type.String({
		description: "Path to the file to edit (relative or absolute)",
	}),
	symbolPath: Type.String({
		description:
			"Symbol path identifying the declaration to anchor against (e.g. function name, class name, ClassName.methodName).",
	}),
	position: Type.Union(
		[
			Type.Literal("BeforeDecl"),
			Type.Literal("AfterDecl"),
			Type.Literal("AtBodyStart"),
			Type.Literal("AtBodyEnd"),
		],
		{
			description:
				"Where to insert relative to the symbol: BeforeDecl (above), AfterDecl (below), AtBodyStart (first line of body), AtBodyEnd (last line of body).",
		},
	),
	code: Type.String({
		description:
			"Code to insert. Include any required leading/trailing newlines for the insertion to land cleanly between the surrounding lines.",
	}),
	lang: Type.Optional(
		Type.String({
			description:
				"Force a specific tree-sitter grammar. Defaults to inferring from file extension.",
		}),
	),
});

type InsertAroundSymbolParams = Static<typeof insertAroundSymbolSchema>;

export function createInsertAroundSymbolTool(
	cwd: string,
): AgentTool<typeof insertAroundSymbolSchema> {
	return {
		name: "InsertAroundSymbol",
		label: "InsertAroundSymbol",
		description:
			"Insert code adjacent to a named declaration: BeforeDecl / AfterDecl / AtBodyStart / AtBodyEnd. Tree-sitter-anchored — survives unrelated edits to the file. Use for adding docstrings, decorators, adjacent declarations, or early-return guards inside a function body. Prefer this over Edit when the insertion point is best described in terms of a named symbol rather than surrounding text.",
		parameters: insertAroundSymbolSchema,
		execute: async (
			_toolCallId: string,
			params: InsertAroundSymbolParams,
			signal?: AbortSignal,
		) => {
			if (signal?.aborted) {
				throw new Error("InsertAroundSymbol cancelled before start");
			}
			const absolutePath = resolveToCwd(cwd, params.path);
			try {
				const result = insertAroundSymbol(
					absolutePath,
					params.symbolPath,
					params.position,
					params.code,
					params.lang ? { lang: params.lang } : undefined,
				);
				if (!result.inserted) {
					return {
						content: [
							{
								type: "text" as const,
								text: `InsertAroundSymbol: no declaration matched symbolPath="${params.symbolPath}" in ${params.path}. Verify the symbol name and that the file's language is supported.`,
							},
						],
						details: { inserted: false, symbolPath: params.symbolPath },
					};
				}
				try {
					notifyFileChanged(absolutePath);
				} catch {
					/* best-effort */
				}
				return {
					content: [
						{
							type: "text" as const,
							text: `InsertAroundSymbol: inserted at ${params.position} of ${params.symbolPath} in ${params.path} (byte offset ${result.byteOffset ?? "?"}).`,
						},
					],
					details: {
						inserted: true,
						symbolPath: params.symbolPath,
						position: params.position,
						byteOffset: result.byteOffset,
					},
				};
			} catch (err: unknown) {
				const message = err instanceof Error ? err.message : String(err);
				return {
					content: [
						{
							type: "text" as const,
							text: `InsertAroundSymbol failed for ${params.path} symbolPath="${params.symbolPath}": ${message}`,
						},
					],
					details: { inserted: false, error: message },
					isError: true,
				};
			}
		},
	};
}
