import { existsSync, readdirSync, readFileSync } from "node:fs";
import { basename, dirname, extname, isAbsolute, join, resolve, } from "node:path";
import { getAgentDir } from "@earendil-works/pi-coding-agent";
import { parse } from "smol-toml";
export const CUSTOM_TOOLS_DIRNAME = "codex-conversion-custom-tools";
const TOOL_NAME_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
const NODE_SCRIPT_PATTERN = /\.(?:cjs|mjs|js)$/i;
export function getCustomToolsDir(agentDir = getAgentDir()) {
    return join(agentDir, CUSTOM_TOOLS_DIRNAME);
}
function requiredString(value, field, path) {
    if (typeof value !== "string" || !value.trim())
        throw new Error(`${path}: ${field} must be a non-empty string`);
    return value.trim();
}
function optionalString(value, field, path) {
    if (value === undefined)
        return undefined;
    return requiredString(value, field, path);
}
function stringArray(value, field, path) {
    if (value === undefined)
        return [];
    if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string"))
        throw new Error(`${path}: ${field} must be an array of strings`);
    return [...value];
}
function inputMode(value, path) {
    if (value === undefined)
        return "arg";
    if (value === "arg" || value === "stdin")
        return value;
    throw new Error(`${path}: input must be "arg" or "stdin"`);
}
function deferLoading(value, path) {
    if (value === undefined)
        return true;
    if (typeof value === "boolean")
        return value;
    throw new Error(`${path}: defer_loading must be a boolean`);
}
export function parseCustomTool(path, text) {
    const name = basename(path, extname(path));
    if (!TOOL_NAME_PATTERN.test(name))
        throw new Error(`${path}: filename must be a JavaScript-compatible tool name`);
    const value = parse(text);
    const known = new Set([
        "usage",
        "description",
        "output",
        "defer_loading",
        "command",
        "args",
        "input",
    ]);
    const unknown = Object.keys(value).filter((key) => !known.has(key));
    if (unknown.length > 0)
        throw new Error(`${path}: unknown field${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}`);
    const command = requiredString(value["command"], "command", path);
    const args = stringArray(value["args"], "args", path);
    const resolvedCommand = command.startsWith(".")
        ? resolve(dirname(path), command)
        : command;
    const nodeScript = isAbsolute(resolvedCommand) && NODE_SCRIPT_PATTERN.test(resolvedCommand);
    return {
        name,
        usage: requiredString(value["usage"], "usage", path),
        description: optionalString(value["description"], "description", path),
        output: optionalString(value["output"], "output", path),
        deferLoading: deferLoading(value["defer_loading"], path),
        command: nodeScript ? process.execPath : resolvedCommand,
        args: nodeScript ? [resolvedCommand, ...args] : args,
        input: inputMode(value["input"], path),
        sourcePath: path,
    };
}
export function discoverCustomTools(dir = getCustomToolsDir()) {
    if (!existsSync(dir))
        return [];
    return readdirSync(dir, { withFileTypes: true })
        .filter((entry) => entry.isFile() && entry.name.endsWith(".toml"))
        .sort((left, right) => left.name.localeCompare(right.name))
        .map((entry) => {
        const path = join(dir, entry.name);
        return parseCustomTool(path, readFileSync(path, "utf8"));
    });
}
