import { readFileSync, existsSync } from 'fs';
import { jsonParse, type INodeTypeDescription } from 'n8n-workflow';
import { join } from 'path';

interface NodeWithVersion extends INodeTypeDescription {
	version: number | number[];
	defaultVersion?: number;
}

// These types are ignored because they tend to cause issues when generating workflows
// Same as in ai-workflow-builder-agent.service.ts
const IGNORED_TYPES = new Set([
	'@n8n/n8n-nodes-langchain.toolVectorStore',
	'@n8n/n8n-nodes-langchain.documentGithubLoader',
	'@n8n/n8n-nodes-langchain.code',
]);

// Parse disabled nodes from environment variable (comma-separated)
function getDisabledNodes(): Set<string> {
	const disabledNodesEnv = process.env.N8N_EVALS_DISABLED_NODES ?? '';
	return new Set(
		disabledNodesEnv
			.split(',')
			.map((s) => s.trim())
			.filter(Boolean),
	);
}

/**
 * Filter node types similar to production service:
 * - Remove ignored types (hardcoded)
 * - Remove disabled types (from env var)
 * - Remove hidden nodes (except DataTable)
 * - Merge tool nodes with their non-tool counterparts
 */
function filterNodeTypes(
	nodeTypes: INodeTypeDescription[],
	disabledNodes: Set<string>,
): INodeTypeDescription[] {
	const visibleNodeTypes = nodeTypes.filter(
		(nodeType) =>
			!IGNORED_TYPES.has(nodeType.name) &&
			!disabledNodes.has(nodeType.name) &&
			// Filter out hidden nodes, except for the Data Table node which has custom hiding logic
			(nodeType.hidden !== true || nodeType.name === 'n8n-nodes-base.dataTable'),
	);

	return visibleNodeTypes.map((nodeType) => {
		// If the node type is a tool, merge it with the corresponding non-tool node type
		const isTool = nodeType.name.endsWith('Tool');
		if (!isTool) return nodeType;

		const nonToolNode = nodeTypes.find((nt) => nt.name === nodeType.name.replace('Tool', ''));
		if (!nonToolNode) return nodeType;

		return { ...nonToolNode, ...nodeType };
	});
}

export function loadNodesFromFile(): INodeTypeDescription[] {
	const preferredPath = join(__dirname, '..', '.data', 'nodes.json');
	const legacyPath = join(__dirname, '..', 'nodes.json');
	const nodesPath = existsSync(preferredPath) ? preferredPath : legacyPath;

	if (!existsSync(nodesPath)) {
		throw new Error(
			`nodes.json not found at ${nodesPath}. ` +
				'Run n8n and export node definitions to evaluations/.data/nodes.json',
		);
	}

	const nodesData = readFileSync(nodesPath, 'utf-8');
	const allNodes = jsonParse<NodeWithVersion[]>(nodesData);

	// Keep all version entries instead of selecting only the latest version.
	// Workflows may use older node versions (e.g., removeDuplicates v1.1), and
	// discarding those entries causes "Node type not found" validation errors.
	const disabledNodes = getDisabledNodes();
	return filterNodeTypes(allNodes, disabledNodes);
}
