// api/monitoring/client/operations/getClients.methods.ts
import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
import { apiRequest } from '../../../../helpers/apiRequest';
import { logger } from '../../../../helpers/logger';
import { handleApiError } from '../../../../helpers/errorHandler';
import { validateClientResponse } from '../client.types';

/**
 * Valid client types for unified client operations
 */
const CLIENT_TYPES = {
	WIRELESS: 'WIRELESS',
	WIRED: 'WIRED',
} as const;

/**
 * Valid client statuses for unified client operations
 */
const CLIENT_STATUSES = {
	CONNECTED: 'CONNECTED',
	FAILED_TO_CONNECT: 'FAILED_TO_CONNECT',
} as const;

/**
 * Validates that only one of the mutually exclusive parameters is provided
 */
function validateMutuallyExclusiveParams(
	params: Record<string, any>,
	exclusiveParams: string[],
): string | null {
	const providedParams = exclusiveParams.filter(
		(param) => params[param] && params[param].toString().trim(),
	);

	if (providedParams.length > 1) {
		return `You can only specify one of: ${exclusiveParams.join(', ')}. Found: ${providedParams.join(', ')}`;
	}

	return null;
}

/**
 * Validates client type and status combination
 */
function validateClientTypeAndStatus(clientType: string, clientStatus: string): string | null {
	if (clientStatus === CLIENT_STATUSES.FAILED_TO_CONNECT && clientType !== CLIENT_TYPES.WIRELESS) {
		return 'Failed to connect status is not supported for wired clients';
	}

	return null;
}

/**
 * Builds query parameters object from UI inputs, filtering out empty values
 */
function buildQueryParameters(
	params: Record<string, any>,
): Record<string, string | number | boolean> {
	const queryParams: Record<string, string | number | boolean> = {};

	for (const [key, value] of Object.entries(params)) {
		if (value !== undefined && value !== null) {
			// Handle string values
			if (typeof value === 'string') {
				const trimmed = value.trim();
				if (trimmed) {
					queryParams[key] = trimmed;
				}
			}
			// Handle boolean values
			else if (typeof value === 'boolean') {
				queryParams[key] = value;
			}
			// Handle number values
			else if (typeof value === 'number' && value >= 0) {
				queryParams[key] = value;
			}
		}
	}

	return queryParams;
}

/**
 * Get a list of Connected wireless clients from Aruba Central
 *
 * @param this The n8n execution context
 * @returns Formatted list of wireless clients
 */
export async function getWirelessClients(this: IExecuteFunctions): Promise<INodeExecutionData[]> {
	try {
		logger.debug('monitoring:client:getWirelessClients', 'Getting wireless clients');

		// Get parameters from UI
		const additionalFields = this.getNodeParameter('additionalFields', 0, {}) as {
			group?: string;
			swarm_id?: string;
			label?: string;
			site?: string;
			network?: string;
			serial?: string;
			os_type?: string;
			cluster_id?: string;
			band?: string;
			fields?: string;
			calculate_total?: boolean;
			offset?: number;
			limit?: number;
			sort?: string;
			last_client_mac?: string;
		};

		// Build query parameters using utility function
		const queryParameters = buildQueryParameters(additionalFields);

		logger.debug('monitoring:client:getWirelessClients', 'Query parameters:', queryParameters);

		// Make API request to get wireless clients
		const response = await apiRequest.call(
			this,
			'GET',
			'/monitoring/v1/clients/wireless',
			{},
			queryParameters,
		);

		logger.debug(
			'monitoring:client:getWirelessClients',
			`Got ${response.clients?.length || 0} wireless clients`,
		);

		// Validate and return the response
		if (validateClientResponse(response)) {
			return [{ json: response }];
		} else {
			logger.warn('monitoring:client:getWirelessClients', 'Response validation failed', response);
			return [{ json: response }];
		}
	} catch (error) {
		return handleApiError.call(this, error, 'Failed to get wireless clients');
	}
}

/**
 * Get a list of Connected wired clients from Aruba Central
 *
 * @param this The n8n execution context
 * @returns Formatted list of wired clients
 */
export async function getWiredClients(this: IExecuteFunctions): Promise<INodeExecutionData[]> {
	try {
		logger.debug('monitoring:client:getWiredClients', 'Getting wired clients');

		// Get parameters from UI
		const additionalFields = this.getNodeParameter('additionalFields', 0, {}) as {
			group?: string;
			swarm_id?: string;
			label?: string;
			site?: string;
			serial?: string;
			cluster_id?: string;
			stack_id?: string;
			fields?: string;
			calculate_total?: boolean;
			offset?: number;
			limit?: number;
			sort?: string;
			last_client_mac?: string;
		};

		// Build query parameters using utility function
		const queryParameters = buildQueryParameters(additionalFields);

		logger.debug('monitoring:client:getWiredClients', 'Query parameters:', queryParameters);

		// Make API request to get wired clients
		const response = await apiRequest.call(
			this,
			'GET',
			'/monitoring/v1/clients/wired',
			{},
			queryParameters,
		);

		logger.debug(
			'monitoring:client:getWiredClients',
			`Got ${response.clients?.length || 0} wired clients`,
		);

		// Validate and return the response
		if (validateClientResponse(response)) {
			return [{ json: response }];
		} else {
			logger.warn('monitoring:client:getWiredClients', 'Response validation failed', response);
			return [{ json: response }];
		}
	} catch (error) {
		return handleApiError.call(this, error, 'Failed to get wired clients');
	}
}

/**
 * Get a list of unified clients from Aruba Central
 * This is a unified form of the wired and wireless client APIs
 *
 * @param this The n8n execution context
 * @returns Formatted list of unified clients
 */
export async function getUnifiedClients(this: IExecuteFunctions): Promise<INodeExecutionData[]> {
	try {
		logger.debug('monitoring:client:getUnifiedClients', 'Getting unified clients');

		// Get required parameters
		const clientType = this.getNodeParameter('client_type', 0) as string;
		const clientStatus = this.getNodeParameter('client_status', 0) as string;
		const timerange = this.getNodeParameter('timerange', 0) as string;

		// Validate client type and status combination
		const statusError = validateClientTypeAndStatus(clientType, clientStatus);
		if (statusError) {
			throw new Error(statusError);
		}

		// Get optional parameters from UI
		const additionalFields = this.getNodeParameter('additionalFields', 0, {}) as {
			group?: string;
			swarm_id?: string;
			label?: string;
			site?: string;
			network?: string;
			cluster_id?: string;
			serial?: string;
			band?: string;
			stack_id?: string;
			os_type?: string;
			fields?: string;
			calculate_total?: boolean;
			offset?: number;
			limit?: number;
			sort?: string;
			last_client_mac?: string;
			show_usage?: boolean;
			show_manufacturer?: boolean;
			show_signal_db?: boolean;
		};

		// Validate mutually exclusive parameters
		const exclusiveError = validateMutuallyExclusiveParams(additionalFields, [
			'group',
			'swarm_id',
			'cluster_id',
			'network',
			'site',
			'label',
		]);
		if (exclusiveError) {
			throw new Error(exclusiveError);
		}

		// Validate wireless-only parameters
		if (clientType !== CLIENT_TYPES.WIRELESS) {
			if (additionalFields.band?.trim()) {
				throw new Error('Band filter is only supported for wireless clients');
			}
			if (additionalFields.show_signal_db !== undefined) {
				throw new Error('Show signal DB is only supported for wireless clients');
			}
		}

		// Validate wired-only parameters
		if (clientType !== CLIENT_TYPES.WIRED) {
			if (additionalFields.stack_id?.trim()) {
				throw new Error('Stack ID filter is only supported for wired clients');
			}
		}

		// Build query parameters using utility function
		const queryParameters = buildQueryParameters({
			client_type: clientType,
			client_status: clientStatus,
			timerange: timerange,
			...additionalFields,
		});

		logger.debug(
			'monitoring:client:getUnifiedClients',
			`Getting ${clientType.toLowerCase()} clients with status ${clientStatus}`,
			queryParameters,
		);

		// Make API request to get unified clients
		const response = await apiRequest.call(
			this,
			'GET',
			'/monitoring/v2/clients',
			{},
			queryParameters,
		);

		logger.debug(
			'monitoring:client:getUnifiedClients',
			`Got ${response.clients?.length || 0} unified ${clientType.toLowerCase()} clients with status ${clientStatus}`,
		);

		// Validate and return the response
		if (validateClientResponse(response)) {
			return [{ json: response }];
		} else {
			logger.warn('monitoring:client:getUnifiedClients', 'Response validation failed', response);
			return [{ json: response }];
		}
	} catch (error) {
		return handleApiError.call(this, error, 'Failed to get unified clients');
	}
}
