import { IExecuteFunctions, IDataObject } from 'n8n-workflow';
import { apiRequest, apiRequestAllItems } from '../helpers/apiRequest';
import { handleProxMoxError, validateParameters } from '../helpers/errorHandler';
import { logger } from '../helpers/logger';

export async function executeStorageOperation(
	this: IExecuteFunctions,
	operation: string,
	i: number,
): Promise<IDataObject | IDataObject[]> {
	try {
		logger.operation('storage', operation, 'start', { operation });

		let response: IDataObject | IDataObject[];

		switch (operation) {
			case 'getAll':
				response = await getAllStorage.call(this, i);
				break;

			case 'get':
				response = await getStorage.call(this, i);
				break;

			case 'getContent':
				response = await getStorageContent.call(this, i);
				break;

			case 'getStatus':
				response = await getStorageStatus.call(this, i);
				break;

			case 'upload':
				response = await uploadToStorage.call(this, i);
				break;

			case 'deleteFile':
				response = await deleteFileFromStorage.call(this, i);
				break;

			default:
				throw new Error(`Unknown storage operation: ${operation}`);
		}

		logger.operation('storage', operation, 'success', { operation });
		return response;
	} catch (error) {
		logger.operation('storage', operation, 'error', { operation, error: error.message });
		return handleProxMoxError.call(this, error, operation, 'storage');
	}
}

async function getAllStorage(this: IExecuteFunctions, i: number): Promise<IDataObject[]> {
	const returnAll = this.getNodeParameter('returnAll', i) as boolean;
	const additionalFields = this.getNodeParameter('additionalFields', i, {}) as IDataObject;

	let qs: IDataObject = {};

	// Add filters
	if (additionalFields.enabled !== undefined) {
		qs.enabled = additionalFields.enabled ? 1 : 0;
	}

	logger.debug('storage', 'Getting all storage configurations', { additionalFields });

	if (returnAll) {
		return await apiRequestAllItems.call(this, 'GET', '/storage', {}, qs);
	} else {
		const limit = this.getNodeParameter('limit', i) as number;
		// ProxMox API doesn't support limit parameter, we'll slice the results client-side
		const response = await apiRequest.call(this, 'GET', '/storage', {}, qs);
		return Array.isArray(response) ? response.slice(0, limit) : [response];
	}
}

async function getStorage(this: IExecuteFunctions, i: number): Promise<IDataObject> {
	const storageId = this.getNodeParameter('storageId', i) as string;

	validateParameters.call(this, { storageId }, ['storageId'], 'get storage');

	logger.debug('storage', 'Getting storage configuration', { storageId });

	return await apiRequest.call(this, 'GET', `/storage/${storageId}`);
}

async function getStorageContent(this: IExecuteFunctions, i: number): Promise<IDataObject[]> {
	const nodeName = this.getNodeParameter('nodeName', i) as string;
	const storageId = this.getNodeParameter('storageId', i) as string;
	const returnAll = this.getNodeParameter('returnAll', i) as boolean;
	const contentType = this.getNodeParameter('contentType', i, '') as string;
	const additionalFields = this.getNodeParameter('additionalFields', i, {}) as IDataObject;

	validateParameters.call(
		this,
		{ nodeName, storageId },
		['nodeName', 'storageId'],
		'get storage content',
	);

	let qs: IDataObject = {};

	if (contentType) {
		qs.content = contentType;
	}

	// Add additional filters
	if (additionalFields.vmid) {
		qs.vmid = additionalFields.vmid;
	}
	if (additionalFields.format) {
		qs.format = additionalFields.format;
	}

	logger.debug('storage', 'Getting storage content', {
		nodeName,
		storageId,
		contentType,
		additionalFields,
	});

	if (returnAll) {
		return await apiRequestAllItems.call(
			this,
			'GET',
			`/nodes/${nodeName}/storage/${storageId}/content`,
			{},
			qs,
		);
	} else {
		const limit = this.getNodeParameter('limit', i) as number;
		// ProxMox API doesn't support limit parameter, we'll slice the results client-side
		const response = await apiRequest.call(
			this,
			'GET',
			`/nodes/${nodeName}/storage/${storageId}/content`,
			{},
			qs,
		);
		return Array.isArray(response) ? response.slice(0, limit) : [response];
	}
}

async function getStorageStatus(this: IExecuteFunctions, i: number): Promise<IDataObject> {
	const nodeName = this.getNodeParameter('nodeName', i) as string;
	const storageId = this.getNodeParameter('storageId', i) as string;

	validateParameters.call(
		this,
		{ nodeName, storageId },
		['nodeName', 'storageId'],
		'get storage status',
	);

	logger.debug('storage', 'Getting storage status', { nodeName, storageId });

	return await apiRequest.call(this, 'GET', `/nodes/${nodeName}/storage/${storageId}/status`);
}

async function uploadToStorage(this: IExecuteFunctions, i: number): Promise<IDataObject> {
	const nodeName = this.getNodeParameter('nodeName', i) as string;
	const storageId = this.getNodeParameter('storageId', i) as string;
	const fileName = this.getNodeParameter('fileName', i) as string;
	const fileContent = this.getNodeParameter('fileContent', i) as string;
	const uploadOptions = this.getNodeParameter('uploadOptions', i, {}) as IDataObject;

	validateParameters.call(
		this,
		{ nodeName, storageId, fileName, fileContent },
		['nodeName', 'storageId', 'fileName', 'fileContent'],
		'upload to storage',
	);

	// Decode base64 content
	let decodedContent: Buffer;
	try {
		decodedContent = Buffer.from(fileContent, 'base64');
	} catch (error) {
		throw new Error(`Invalid base64 file content: ${error.message}`);
	}

	const body: IDataObject = {
		filename: fileName,
		content: uploadOptions.content || 'iso',
		...uploadOptions,
	};

	// Handle checksum validation if provided
	if (uploadOptions.checksum) {
		const [algorithm, hash] = (uploadOptions.checksum as string).split(':');
		if (algorithm && hash) {
			body.checksum = hash;
			body['checksum-algorithm'] = algorithm;
		}
	}

	logger.debug('storage', 'Uploading file to storage', {
		nodeName,
		storageId,
		fileName,
		size: decodedContent.length,
	});

	// Note: This is a simplified implementation. In a real scenario, you would need to handle
	// multipart form data upload with the actual file content. The ProxMox API expects
	// a multipart/form-data request with the file as a binary attachment.
	return await apiRequest.call(this, 'POST', `/nodes/${nodeName}/storage/${storageId}/upload`, {
		...body,
		content: decodedContent.toString('base64'),
	});
}

async function deleteFileFromStorage(this: IExecuteFunctions, i: number): Promise<IDataObject> {
	const nodeName = this.getNodeParameter('nodeName', i) as string;
	const storageId = this.getNodeParameter('storageId', i) as string;
	const filePath = this.getNodeParameter('filePath', i) as string;

	validateParameters.call(
		this,
		{ nodeName, storageId, filePath },
		['nodeName', 'storageId', 'filePath'],
		'delete file from storage',
	);

	// The volume ID format is typically storage:path
	const volumeId = `${storageId}:${filePath}`;

	logger.debug('storage', 'Deleting file from storage', {
		nodeName,
		storageId,
		filePath,
		volumeId,
	});

	return await apiRequest.call(
		this,
		'DELETE',
		`/nodes/${nodeName}/storage/${storageId}/content/${encodeURIComponent(volumeId)}`,
	);
}
