import { KnowledgeSearch, NodeSchemaProvider, WorkflowValidator, } from '@n8n-as-code/skills';
import { N8nApiClient } from 'n8nac';
export class N8nEngine {
    name = 'n8n';
    config;
    nodeProvider;
    knowledgeSearch;
    workflowValidator;
    apiClient;
    constructor(config) {
        this.config = config;
        this.nodeProvider = new NodeSchemaProvider();
        this.knowledgeSearch = new KnowledgeSearch();
        this.workflowValidator = new WorkflowValidator();
        const credentials = {
            host: config.host,
            apiKey: config.apiKey,
        };
        this.apiClient = new N8nApiClient(credentials);
    }
    async searchNodes(query) {
        return this.nodeProvider.searchNodes(query).map((node) => ({
            name: node.name,
            type: node.type,
            displayName: node.displayName,
            description: node.description,
        }));
    }
    async nodeInfo(type) {
        const schema = this.nodeProvider.getNodeSchema(type);
        if (!schema) {
            throw new Error(`Node schema not found for type: ${type}`);
        }
        return schema;
    }
    async searchTemplates(query) {
        const result = this.knowledgeSearch.searchAll(query, {
            type: 'documentation',
            category: 'advanced-ai',
            limit: 10,
        });
        return result.results.map((item) => ({
            id: item.id,
            title: item.title || item.name || item.displayName || item.id,
            excerpt: item.excerpt,
            category: item.category,
            url: item.url,
        }));
    }
    async generateWorkflow(spec) {
        const nodes = spec.nodes.map((node, index) => ({
            id: `${index + 1}`,
            name: node.name,
            type: node.type,
            typeVersion: node.typeVersion ?? 1,
            position: node.position ?? [index * 240, index * 120],
            parameters: node.parameters,
            credentials: this.normalizeCredentials(node.credentials),
        }));
        const connections = this.normalizeConnections(spec.connections);
        const definition = {
            name: spec.name,
            nodes,
            connections,
            active: spec.active ?? false,
            settings: {},
            versionId: '',
            id: '',
            pinData: {},
            tags: [],
        };
        return {
            engine: 'n8n',
            name: spec.name,
            sourceType: 'n8n-json',
            definition,
            credentialRequirements: this.extractCredentialRequirements(spec),
        };
    }
    normalizeConnections(specConnections) {
        if (Array.isArray(specConnections)) {
            return this.convertEdgeListToN8nConnections(specConnections);
        }
        const values = Object.values(specConnections);
        const isGroupedEdgeList = values.every((value) => Array.isArray(value));
        if (isGroupedEdgeList) {
            const flattened = Object.values(specConnections)
                .flat();
            return this.convertEdgeListToN8nConnections(flattened);
        }
        return specConnections;
    }
    convertEdgeListToN8nConnections(connections) {
        return connections.reduce((acc, connection) => {
            const connectionType = connection.type ?? 'main';
            if (!acc[connection.from]) {
                acc[connection.from] = {};
            }
            if (!acc[connection.from][connectionType]) {
                acc[connection.from][connectionType] = [[]];
            }
            acc[connection.from][connectionType][0].push({
                node: connection.to,
                type: connectionType,
                index: connection.index ?? 0,
            });
            return acc;
        }, {});
    }
    async validate(workflow) {
        if (workflow.sourceType !== 'n8n-json') {
            throw new Error('N8nEngine can only validate n8n-json workflows');
        }
        const result = await this.workflowValidator.validateWorkflow(workflow.definition);
        return {
            valid: result.valid,
            errors: result.errors,
            warnings: result.warnings,
        };
    }
    async deploy(workflow) {
        if (workflow.sourceType !== 'n8n-json') {
            throw new Error('N8nEngine can only deploy n8n-json workflows');
        }
        const definition = workflow.definition;
        const created = await this.apiClient.createWorkflow(definition);
        if (definition.active) {
            await this.apiClient.activateWorkflow(created.id, true);
        }
        return {
            id: created.id,
            engine: 'n8n',
            name: created.name,
            active: Boolean(definition.active),
            workflowUrl: `${this.config.host.replace(/\/$/, '')}/workflow/${created.id}`,
            credentialRequirements: workflow.credentialRequirements,
        };
    }
    async listWorkflows() {
        const workflows = await this.apiClient.getAllWorkflows(this.config.projectId);
        return workflows.map((workflow) => ({
            id: workflow.id,
            engine: 'n8n',
            name: workflow.name,
            active: workflow.active,
            workflowUrl: `${this.config.host.replace(/\/$/, '')}/workflow/${workflow.id}`,
            credentialRequirements: [],
        }));
    }
    async activateWorkflow(id) {
        const ok = await this.apiClient.activateWorkflow(id, true);
        if (!ok) {
            throw new Error(`Failed to activate workflow ${id}`);
        }
    }
    async deactivateWorkflow(id) {
        const ok = await this.apiClient.activateWorkflow(id, false);
        if (!ok) {
            throw new Error(`Failed to deactivate workflow ${id}`);
        }
    }
    async deleteWorkflow(id) {
        const ok = await this.apiClient.deleteWorkflow(id);
        if (!ok) {
            throw new Error(`Failed to delete workflow ${id}`);
        }
    }
    extractCredentialRequirements(spec) {
        const requirements = [];
        for (const node of spec.nodes) {
            const credentials = node.credentials || {};
            for (const [credentialType, credentialRef] of Object.entries(credentials)) {
                requirements.push({
                    nodeName: node.name,
                    credentialType,
                    displayName: credentialRef.name || credentialType,
                    required: true,
                    status: credentialRef.id || credentialRef.name ? 'linked' : 'missing',
                });
            }
        }
        return requirements;
    }
    normalizeCredentials(credentials) {
        if (!credentials) {
            return undefined;
        }
        const normalized = Object.entries(credentials).reduce((acc, [credentialType, credentialRef]) => {
            if (credentialRef.id && credentialRef.name) {
                acc[credentialType] = {
                    id: credentialRef.id,
                    name: credentialRef.name,
                };
            }
            return acc;
        }, {});
        return Object.keys(normalized).length > 0 ? normalized : undefined;
    }
}
//# sourceMappingURL=n8n-engine.js.map