// firmware/upgrade/operations/upgradeFirmware.methods.ts
import {
	IExecuteFunctions,
	INodeExecutionData,
	IDataObject,
	NodeOperationError,
} from 'n8n-workflow';
import { apiRequest } from '../../../../helpers/apiRequest';
import { logger } from '../../../../helpers/logger';

/**
 * Initiates a firmware upgrade for a device or group
 * POST /firmware/v1/upgrade
 *
 * @param this The n8n execution context
 * @returns Result of the firmware upgrade operation
 */
export async function upgradeFirmware(this: IExecuteFunctions): Promise<INodeExecutionData[]> {
	try {
		// Get parameters
		const targetType = this.getNodeParameter('targetType', 0) as string;
		const targetValue = this.getNodeParameter('targetValue', 0) as string;
		const firmwareVersion = this.getNodeParameter('firmwareVersion', 0, '') as string;

		// Get device type only when target is a group, or to determine AP devices
		let deviceType: string | undefined;
		if (targetType === 'group') {
			deviceType = this.getNodeParameter('deviceType', 0) as string;
		} else {
			// For serial/swarm_id targets, check if we need to know the device type
			// This is needed to determine if we should use serial or swarm_id for APs
			try {
				deviceType = this.getNodeParameter('deviceType', 0, '') as string;
			} catch (e) {
				// Device type might not be available for non-group targets, which is OK
				deviceType = '';
			}
		}

		const options = this.getNodeParameter('options', 0, {}) as IDataObject;

		// Create request body
		const body: IDataObject = {};

		// Add firmware version if specified
		if (firmwareVersion && firmwareVersion.trim() !== '') {
			body.firmware_version = firmwareVersion;
		}

		// Add target parameter based on type
		if (targetType === 'group') {
			// For group-level upgrades, we need both device_type and group
			body.device_type = deviceType;
			body.group = targetValue;
		} else if (targetType === 'swarm_id') {
			// For swarm upgrades, just use swarm_id
			body.swarm_id = targetValue;
		} else if (targetType === 'serial') {
			// Check if this is an AP - if so, use swarm_id instead of serial
			if (deviceType === 'IAP') {
				body.swarm_id = targetValue;
				console.log('Converting serial to swarm_id for IAP device');
			} else {
				body.serial = targetValue;
			}
		}

		// Add optional parameters
		if (options.reboot !== undefined) {
			body.reboot = options.reboot;
		}

		if (options.model) {
			body.model = options.model;
		}

		if (options.firmwareScheduledAt) {
			const scheduledTime = parseInt(options.firmwareScheduledAt as string, 10);
			if (!isNaN(scheduledTime)) {
				body.firmware_scheduled_at = scheduledTime;
			}
		}

		// Parse JSON string arrays into actual arrays
		if (options.excludeGroups) {
			try {
				body.exclude_groups = JSON.parse(options.excludeGroups as string);
			} catch (e) {
				body.exclude_groups = [options.excludeGroups];
			}
		}

		if (options.excludeCustomers) {
			try {
				body.exclude_customers = JSON.parse(options.excludeCustomers as string);
			} catch (e) {
				body.exclude_customers = [options.excludeCustomers];
			}
		}

		// Log the request for detailed debugging
		console.log('===== FIRMWARE UPGRADE REQUEST DETAILS =====');
		console.log('URL: /firmware/v1/upgrade');
		console.log('Method: POST');
		console.log('Body:', JSON.stringify(body, null, 2));
		console.log('===========================================');

		// Make the API request
		try {
			const response = await apiRequest.call(this, 'POST', '/firmware/v1/upgrade', body);

			console.log('===== FIRMWARE UPGRADE SUCCESS =====');
			console.log(JSON.stringify(response, null, 2));
			console.log('===================================');

			return [{ json: response }];
		} catch (requestError) {
			// Handle rate limiting errors specifically
			if (requestError.message && requestError.message.includes('rate limit')) {
				// Extract retry time if available
				const retryMatch = requestError.message.match(/after (\d+) seconds/);
				const retrySeconds = retryMatch ? retryMatch[1] : 'some time';

				throw new NodeOperationError(
					this.getNode(),
					`API rate limited. Please try again after ${retrySeconds} seconds.`,
					{
						description: requestError.message,
					},
				);
			}

			// For other API errors
			console.error('===== FIRMWARE UPGRADE API ERROR =====');
			console.error('Status Code:', requestError.statusCode || 'Unknown');

			if (requestError.response) {
				console.error(
					'Response Body:',
					typeof requestError.response.body === 'object'
						? JSON.stringify(requestError.response.body, null, 2)
						: requestError.response.body,
				);
			}

			console.error('Error Message:', requestError.message);
			console.error('=====================================');

			// Re-throw with better context
			throw new NodeOperationError(this.getNode(), `API Error: ${requestError.message}`, {
				description: requestError.response?.body
					? JSON.stringify(requestError.response.body)
					: 'See logs for details',
			});
		}
	} catch (error) {
		// If it's already a NodeOperationError, just re-throw it
		if (error.name === 'NodeOperationError') {
			throw error;
		}

		// Final catch-all error handling
		console.error('===== FIRMWARE UPGRADE FUNCTION ERROR =====');
		console.error('Error Type:', error.constructor.name);
		console.error('Error Message:', error.message);
		console.error('=========================================');

		throw new NodeOperationError(this.getNode(), `Firmware upgrade failed: ${error.message}`);
	}
}
