"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.listNodeDiscriminators = listNodeDiscriminators;
exports.resolveNodeTypeDefinition = resolveNodeTypeDefinition;
exports.resolveBuiltinNodeDefinitionDirs = resolveBuiltinNodeDefinitionDirs;
const backend_common_1 = require("@n8n/backend-common");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
function isValidPathComponent(component) {
    if (!component || component.trim() === '')
        return false;
    if (component.includes('\0'))
        return false;
    if (component.includes('/') || component.includes('\\'))
        return false;
    if (component === '..' || component.startsWith('..'))
        return false;
    return true;
}
function parseNodeId(nodeId) {
    if (nodeId.startsWith('@n8n/')) {
        const withoutPrefix = nodeId.slice(5);
        const dotIndex = withoutPrefix.indexOf('.');
        if (dotIndex === -1)
            return null;
        return {
            packageName: withoutPrefix.slice(0, dotIndex),
            nodeName: withoutPrefix.slice(dotIndex + 1),
        };
    }
    const dotIndex = nodeId.indexOf('.');
    if (dotIndex === -1)
        return null;
    return {
        packageName: nodeId.slice(0, dotIndex),
        nodeName: nodeId.slice(dotIndex + 1),
    };
}
function toSnakeCase(str) {
    return str
        .replace(/([A-Z])/g, '_$1')
        .toLowerCase()
        .replace(/^_/, '')
        .replace(/[-\s]+/g, '_');
}
function getNodesPaths(nodeDefinitionDirs) {
    return nodeDefinitionDirs.map((dir) => (0, backend_common_1.safeJoinPath)(dir, 'nodes'));
}
function findNodeDir(parsed, nodesPaths) {
    for (const nodesPath of nodesPaths) {
        try {
            const nodeDir = (0, backend_common_1.safeJoinPath)(nodesPath, parsed.packageName, parsed.nodeName);
            if ((0, node_fs_1.existsSync)(nodeDir))
                return { nodesPath, nodeDir };
        }
        catch {
            continue;
        }
    }
    if (parsed.nodeName.endsWith('Tool')) {
        const baseName = parsed.nodeName.slice(0, -4);
        for (const nodesPath of nodesPaths) {
            try {
                const nodeDir = (0, backend_common_1.safeJoinPath)(nodesPath, parsed.packageName, baseName);
                if ((0, node_fs_1.existsSync)(nodeDir))
                    return { nodesPath, nodeDir };
            }
            catch {
                continue;
            }
        }
    }
    return null;
}
function getNodeVersions(nodeId, nodeDefinitionDirs) {
    const parsed = parseNodeId(nodeId);
    if (!parsed)
        return [];
    const nodesPaths = getNodesPaths(nodeDefinitionDirs);
    const found = findNodeDir(parsed, nodesPaths);
    if (!found)
        return [];
    try {
        const entries = (0, node_fs_1.readdirSync)(found.nodeDir, { withFileTypes: true });
        const versions = [];
        for (const entry of entries) {
            if (entry.isFile() &&
                entry.name.startsWith('v') &&
                entry.name.endsWith('.ts') &&
                entry.name !== 'index.ts' &&
                !entry.name.endsWith('.schema.js')) {
                versions.push(entry.name.replace('.ts', ''));
            }
            else if (entry.isDirectory() && /^v\d+$/.test(entry.name)) {
                versions.push(entry.name);
            }
        }
        versions.sort((a, b) => {
            const aNum = parseInt(a.slice(1), 10);
            const bNum = parseInt(b.slice(1), 10);
            return bNum - aNum;
        });
        return versions;
    }
    catch {
        return [];
    }
}
function tryResolveNodeFilePath(nodeId, version, nodeDefinitionDirs, discriminators) {
    const parsed = parseNodeId(nodeId);
    if (!parsed)
        return { error: `Invalid node ID format: '${nodeId}'` };
    if (!isValidPathComponent(parsed.packageName) || !isValidPathComponent(parsed.nodeName)) {
        return { error: `Invalid node ID: '${nodeId}'` };
    }
    const nodesPaths = getNodesPaths(nodeDefinitionDirs);
    const found = findNodeDir(parsed, nodesPaths);
    if (!found) {
        return {
            error: `Node type '${nodeId}' not found. Use search-nodes to find the correct node ID.`,
        };
    }
    try {
        return resolveFilePath(nodeId, version, found.nodeDir, nodeDefinitionDirs, discriminators);
    }
    catch {
        return { error: 'Invalid path - path traversal detected' };
    }
}
function resolveResourceOperationFile(nodeId, nodeDir, targetVersion, resources, discriminators) {
    if (!discriminators?.resource || !discriminators?.operation) {
        return {
            error: `Node '${nodeId}' requires resource and operation discriminators. Available resources: ${resources.join(', ')}.`,
        };
    }
    if (!isValidPathComponent(discriminators.resource) ||
        !isValidPathComponent(discriminators.operation)) {
        return { error: 'Invalid discriminator value' };
    }
    const resourceDir = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion, `resource_${toSnakeCase(discriminators.resource)}`);
    if (!(0, node_fs_1.existsSync)(resourceDir)) {
        return {
            error: `Invalid resource '${discriminators.resource}' for node '${nodeId}'. Available: ${resources.join(', ')}`,
        };
    }
    const filePath = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion, `resource_${toSnakeCase(discriminators.resource)}`, `operation_${toSnakeCase(discriminators.operation)}.ts`);
    if (!(0, node_fs_1.existsSync)(filePath)) {
        const ops = (0, node_fs_1.readdirSync)(resourceDir)
            .filter((f) => f.startsWith('operation_') && f.endsWith('.ts'))
            .map((f) => f.replace('operation_', '').replace('.ts', ''));
        return {
            error: `Invalid operation '${discriminators.operation}' for resource '${discriminators.resource}'. Available: ${ops.join(', ')}`,
        };
    }
    return { filePath };
}
function resolveModeFile(nodeId, nodeDir, targetVersion, modes, discriminators) {
    if (!discriminators?.mode) {
        return {
            error: `Node '${nodeId}' requires mode discriminator. Available modes: ${modes.join(', ')}.`,
        };
    }
    if (!isValidPathComponent(discriminators.mode)) {
        return { error: 'Invalid mode value' };
    }
    const filePath = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion, `mode_${toSnakeCase(discriminators.mode)}.ts`);
    if (!(0, node_fs_1.existsSync)(filePath)) {
        return {
            error: `Invalid mode '${discriminators.mode}' for node '${nodeId}'. Available: ${modes.join(', ')}`,
        };
    }
    return { filePath };
}
function resolveFilePath(nodeId, version, nodeDir, nodeDefinitionDirs, discriminators) {
    let targetVersion = version;
    if (!targetVersion) {
        const versions = getNodeVersions(nodeId, nodeDefinitionDirs);
        if (versions.length === 0)
            return { error: `No versions found for node '${nodeId}'` };
        targetVersion = versions[0];
    }
    if (!targetVersion.startsWith('v')) {
        targetVersion = `v${targetVersion.replace('.', '')}`;
    }
    else {
        targetVersion = `v${targetVersion.slice(1).replace('.', '')}`;
    }
    const versionDir = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion);
    const isSplit = (0, node_fs_1.existsSync)(versionDir) && (0, node_fs_1.statSync)(versionDir).isDirectory();
    if (isSplit) {
        const entries = (0, node_fs_1.readdirSync)(versionDir, { withFileTypes: true });
        const resources = entries
            .filter((e) => e.isDirectory() && e.name.startsWith('resource_'))
            .map((e) => e.name.replace('resource_', ''));
        const modes = entries
            .filter((e) => e.isFile() && e.name.startsWith('mode_') && e.name.endsWith('.ts'))
            .map((e) => e.name.replace('mode_', '').replace('.ts', ''));
        if (resources.length > 0) {
            return resolveResourceOperationFile(nodeId, nodeDir, targetVersion, resources, discriminators);
        }
        if (modes.length > 0) {
            return resolveModeFile(nodeId, nodeDir, targetVersion, modes, discriminators);
        }
        return { error: `Node '${nodeId}' has split structure but no recognized discriminators` };
    }
    const filePath = (0, backend_common_1.safeJoinPath)(nodeDir, `${targetVersion}.ts`);
    if (!(0, node_fs_1.existsSync)(filePath)) {
        return { error: `Version '${version}' not found for node '${nodeId}'` };
    }
    return { filePath };
}
function listNodeDiscriminators(nodeId, nodeDefinitionDirs) {
    const parsed = parseNodeId(nodeId);
    if (!parsed)
        return null;
    if (!isValidPathComponent(parsed.packageName) || !isValidPathComponent(parsed.nodeName))
        return null;
    const nodesPaths = getNodesPaths(nodeDefinitionDirs);
    const found = findNodeDir(parsed, nodesPaths);
    if (!found)
        return null;
    const { nodeDir } = found;
    const versions = getNodeVersions(nodeId, nodeDefinitionDirs);
    if (versions.length === 0)
        return null;
    const versionDir = (0, backend_common_1.safeJoinPath)(nodeDir, versions[0]);
    if (!(0, node_fs_1.existsSync)(versionDir) || !(0, node_fs_1.statSync)(versionDir).isDirectory())
        return null;
    const entries = (0, node_fs_1.readdirSync)(versionDir, { withFileTypes: true });
    const resourceDirs = entries.filter((e) => e.isDirectory() && e.name.startsWith('resource_'));
    if (resourceDirs.length === 0)
        return null;
    const resources = resourceDirs.map((dir) => {
        const resourceName = dir.name.replace('resource_', '');
        const resourcePath = (0, backend_common_1.safeJoinPath)(versionDir, dir.name);
        const ops = (0, node_fs_1.readdirSync)(resourcePath)
            .filter((f) => f.startsWith('operation_') && f.endsWith('.ts'))
            .map((f) => f.replace('operation_', '').replace('.ts', ''));
        return { name: resourceName, operations: ops };
    });
    return { resources };
}
function resolveNodeTypeDefinition(nodeId, nodeDefinitionDirs, options) {
    const nodesPaths = getNodesPaths(nodeDefinitionDirs);
    if (!nodesPaths.some((p) => (0, node_fs_1.existsSync)(p))) {
        return {
            content: '',
            error: 'Node types directory not found. Types may not have been generated yet.',
        };
    }
    const discriminators = options
        ? { resource: options.resource, operation: options.operation, mode: options.mode }
        : undefined;
    let result = tryResolveNodeFilePath(nodeId, options?.version, nodeDefinitionDirs, discriminators);
    if (result.error && nodeId.endsWith('Tool')) {
        const baseNodeId = nodeId.slice(0, -4);
        result = tryResolveNodeFilePath(baseNodeId, options?.version, nodeDefinitionDirs, discriminators);
    }
    if (result.error || !result.filePath) {
        return { content: '', error: result.error ?? `Node type '${nodeId}' not found.` };
    }
    try {
        const content = (0, node_fs_1.readFileSync)(result.filePath, 'utf-8');
        const actualVersion = result.filePath.match(/\/(v\d+)(?:\/|\.ts)/)?.[1];
        return { content, version: actualVersion };
    }
    catch (error) {
        return {
            content: '',
            error: `Error reading node definition for '${nodeId}': ${error instanceof Error ? error.message : 'Unknown error'}`,
        };
    }
}
function resolveBuiltinNodeDefinitionDirs() {
    const dirs = [];
    for (const packageId of ['n8n-nodes-base', '@n8n/n8n-nodes-langchain']) {
        try {
            const packageJsonPath = require.resolve(`${packageId}/package.json`);
            const distDir = (0, node_path_1.dirname)(packageJsonPath);
            const nodeDefsDir = (0, backend_common_1.safeJoinPath)(distDir, 'dist', 'node-definitions');
            if ((0, node_fs_1.existsSync)(nodeDefsDir)) {
                dirs.push(nodeDefsDir);
            }
        }
        catch {
        }
    }
    return dirs;
}
//# sourceMappingURL=node-definition-resolver.js.map