import { DuplicateToolError } from "./errors.js";
import { assertValidToolName } from "./tool-names.js";
function normalizeName(name) {
    return name.trim();
}
function isAsyncGeneratorResult(value) {
    if (typeof value !== "object" || value === null) {
        return false;
    }
    const candidate = value;
    return (typeof candidate.next === "function" &&
        typeof candidate.return === "function" &&
        typeof candidate.throw === "function" &&
        typeof candidate[Symbol.asyncIterator] === "function");
}
function createNonStreamingInvocation(result) {
    return (async function* () {
        yield* [];
        return await result;
    })();
}
function createFailingInvocation(error) {
    return (async function* () {
        yield* [];
        throw error;
    })();
}
function normalizeActiveSkills(activeSkills) {
    const seen = new Set();
    const normalized = [];
    for (const skillName of activeSkills ?? []) {
        const name = normalizeName(skillName);
        if (name.length === 0 || seen.has(name)) {
            continue;
        }
        seen.add(name);
        normalized.push(name);
    }
    return normalized;
}
function skillMatchesToolName(skillName, toolName) {
    if (skillName === toolName) {
        return true;
    }
    if (skillName.endsWith(".*")) {
        const namespace = skillName.slice(0, -2).trim();
        return namespace.length > 0 && toolNameHasNamespace(toolName, namespace);
    }
    return toolNameHasNamespace(toolName, skillName);
}
function toolNameHasNamespace(toolName, namespace) {
    return toolName.startsWith(`${namespace}.`) || toolName.startsWith(`${namespace}_`);
}
function isToolVisibleToModel(tool, activeSkills) {
    if (tool.visibility === "model") {
        return true;
    }
    if (tool.visibility === "internal") {
        return false;
    }
    for (const activeSkill of activeSkills) {
        if (skillMatchesToolName(activeSkill, tool.name)) {
            return true;
        }
    }
    return false;
}
export function normalizeTool(tool) {
    assertValidToolName(tool.name);
    const normalizedName = normalizeName(tool.name);
    const call = tool.call.bind(tool);
    return {
        name: normalizedName,
        description: tool.description,
        inputSchema: tool.inputSchema,
        visibility: tool.visibility ?? "model",
        policy: tool.policy,
        invoke(args, ctx) {
            try {
                const result = call(args, ctx);
                if (isAsyncGeneratorResult(result)) {
                    return result;
                }
                return createNonStreamingInvocation(result);
            }
            catch (error) {
                return createFailingInvocation(error);
            }
        }
    };
}
export class ToolRegistry {
    #tools = new Map();
    register(tool) {
        const normalizedTool = Object.freeze(normalizeTool(tool));
        if (this.#tools.has(normalizedTool.name)) {
            throw new DuplicateToolError(normalizedTool.name);
        }
        this.#tools.set(normalizedTool.name, normalizedTool);
    }
    get(name) {
        return this.#tools.get(normalizeName(name));
    }
    getAll() {
        return Array.from(this.#tools.values());
    }
    getActiveTools(activeSkills) {
        const normalizedActiveSkills = normalizeActiveSkills(activeSkills);
        return this.getAll().filter((tool) => isToolVisibleToModel(tool, normalizedActiveSkills));
    }
    copyFrom(registry) {
        for (const tool of registry.#tools.values()) {
            this.#tools.set(tool.name, tool);
        }
    }
}
