import {
	IExecuteFunctions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
	NodeOperationError,
} from 'n8n-workflow';

import { clusterOperations, clusterFields } from './descriptions/cluster.descriptions';
import { vmOperations, vmFields } from './descriptions/vm.descriptions';
import { containerOperations, containerFields } from './descriptions/container.descriptions';
import { storageOperations, storageFields } from './descriptions/storage.descriptions';
import { backupOperations, backupFields } from './descriptions/backup.descriptions';

import { executeClusterOperation } from './methods/cluster.methods';
import { executeVmOperation } from './methods/vm.methods';
import { executeContainerOperation } from './methods/container.methods';
import { executeStorageOperation } from './methods/storage.methods';
import { executeBackupOperation } from './methods/backup.methods';

import { logger } from './helpers/logger';

export class ProxMox implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'ProxMox VE',
		name: 'proxMox',
		icon: 'file:ProxMox.svg',
		group: ['transform'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description:
			'Interact with ProxMox Virtual Environment API for managing VMs, containers, storage, and cluster operations',
		defaults: {
			name: 'ProxMox VE',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'proxMoxApi',
				required: true,
			},
		],
		requestDefaults: {
			baseURL: '={{$credentials.host}}:{{$credentials.port}}/api2/json',
			headers: {
				Accept: 'application/json',
				'Content-Type': 'application/json',
			},
		},
		usableAsTool: true,
		properties: [
			{
				displayName: 'Resource',
				name: 'resource',
				type: 'options',
				noDataExpression: true,
				options: [
					{
						name: 'Cluster',
						value: 'cluster',
						description: 'Manage cluster operations',
					},
					{
						name: 'Virtual Machine',
						value: 'vm',
						description: 'Manage virtual machines (QEMU/KVM)',
					},
					{
						name: 'Container',
						value: 'container',
						description: 'Manage LXC containers',
					},
					{
						name: 'Storage',
						value: 'storage',
						description: 'Manage storage and file operations',
					},
					{
						name: 'Backup',
						value: 'backup',
						description: 'Manage backups and restore operations',
					},
				],
				default: 'vm',
				required: true,
			},

			// Cluster operations
			clusterOperations,
			...clusterFields,

			// VM operations
			vmOperations,
			...vmFields,

			// Container operations
			containerOperations,
			...containerFields,

			// Storage operations
			storageOperations,
			...storageFields,

			// Backup operations
			backupOperations,
			...backupFields,

			// Continue on Fail
			{
				displayName: 'Continue on Fail',
				name: 'continueOnFail',
				type: 'boolean',
				default: false,
				description: 'Whether to continue the workflow if this node fails',
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		console.log('\n\n========== PROXMOX NODE EXECUTION STARTED ==========');

		try {
			const items = this.getInputData();
			console.log('Input data retrieved:', { itemCount: items.length });

			const returnData: INodeExecutionData[] = [];
			const length = items.length;

			console.log('Attempting to get resource parameter...');
			const resource = this.getNodeParameter('resource', 0) as string;
			console.log('Resource parameter retrieved:', resource);

			console.log('Attempting to get operation parameter...');
			const operation = this.getNodeParameter('operation', 0) as string;
			console.log('Operation parameter retrieved:', operation);

			logger.info('node', `ProxMox node execution started`, {
				resource,
				operation,
				itemCount: length,
			});

			for (let i = 0; i < length; i++) {
				console.log(`Processing item ${i + 1}/${length}`);
				try {
					let responseData;

					console.log(`Executing ${resource} - ${operation} operation`);
					switch (resource) {
						case 'cluster':
							console.log('Calling executeClusterOperation...');
							responseData = await executeClusterOperation.call(this, operation, i);
							break;

						case 'vm':
							console.log('Calling executeVmOperation...');
							responseData = await executeVmOperation.call(this, operation, i);
							break;

						case 'container':
							console.log('Calling executeContainerOperation...');
							responseData = await executeContainerOperation.call(this, operation, i);
							break;

						case 'storage':
							console.log('Calling executeStorageOperation...');
							responseData = await executeStorageOperation.call(this, operation, i);
							break;

						case 'backup':
							console.log('Calling executeBackupOperation...');
							responseData = await executeBackupOperation.call(this, operation, i);
							break;

						default:
							throw new NodeOperationError(
								this.getNode(),
								`The resource "${resource}" is not known!`,
								{ itemIndex: i },
							);
					}

					console.log('Operation completed successfully, processing response...');

					// Handle array responses
					if (Array.isArray(responseData)) {
						responseData.forEach((item) => {
							returnData.push({
								json: item,
								pairedItem: { item: i },
							});
						});
					} else {
						returnData.push({
							json: responseData,
							pairedItem: { item: i },
						});
					}
				} catch (error) {
					console.log('=== ERROR CAUGHT IN ITEM PROCESSING ===');
					console.log('Error object:', error);
					console.log('Error message:', error.message);
					console.log('Error stack:', error.stack);
					console.log('Error type:', typeof error);
					console.log('Error constructor:', error.constructor.name);

					logger.error('node', `ProxMox node execution failed`, {
						resource,
						operation,
						itemIndex: i,
						error: error.message,
					});

					if (this.continueOnFail()) {
						returnData.push({
							json: {
								error: error.message,
								itemIndex: i,
								resource,
								operation,
							},
							pairedItem: { item: i },
						});
						continue;
					}
					throw error;
				}
			}

			logger.info('node', `ProxMox node execution completed`, {
				resource,
				operation,
				itemCount: length,
				resultCount: returnData.length,
			});

			console.log('========== PROXMOX NODE EXECUTION COMPLETED ==========\n\n');
			return [returnData];
		} catch (globalError) {
			console.log('ERROR in node execution:');
			console.log(globalError);

			if (this.continueOnFail()) {
				console.log('Continue on fail is enabled, returning error as output');
				return [[{ json: { error: globalError.message } }]];
			}
			throw globalError;
		}
	}
}
