// getEvents.methods.ts
import { IExecuteFunctions, INodeExecutionData, IDataObject } from 'n8n-workflow';
import { apiRequest } from '../../../../helpers/apiRequest';
import { logger } from '../../../../helpers/logger';
import { handleApiError } from '../../../../helpers/errorHandler';

/**
 * Get Events
 *
 * GET /monitoring/v2/events
 *
 * @param this The n8n execution context
 * @returns Formatted API response
 */
export async function getEvents(this: IExecuteFunctions): Promise<INodeExecutionData[]> {
	try {
		const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean;
		const limit = returnAll ? 0 : (this.getNodeParameter('limit', 0, 50) as number);

		// Get additional fields
		const additionalFields = this.getNodeParameter('additionalFields', 0, {}) as IDataObject;

		// Construct request options with query parameters
		const qs: IDataObject = {};

		// Set pagination parameters
		if (!returnAll && limit > 0) {
			qs.limit = limit;
		}

		// Handle timestamp conversion from dateTime to epoch seconds
		if (additionalFields.from_timestamp !== undefined && additionalFields.from_timestamp !== '') {
			const fromDate = new Date(additionalFields.from_timestamp as string);
			qs.from_timestamp = Math.floor(fromDate.getTime() / 1000);
			logger.debug('parameter:from_timestamp', `Setting from_timestamp to ${qs.from_timestamp}`);
		}

		if (additionalFields.to_timestamp !== undefined && additionalFields.to_timestamp !== '') {
			const toDate = new Date(additionalFields.to_timestamp as string);
			qs.to_timestamp = Math.floor(toDate.getTime() / 1000);
			logger.debug('parameter:to_timestamp', `Setting to_timestamp to ${qs.to_timestamp}`);
		}

		// Add all other additional fields to query parameters
		const paramMappings: { [key: string]: string } = {
			group: 'group',
			swarm_id: 'swarm_id',
			label: 'label',
			macaddr: 'macaddr',
			bssid: 'bssid',
			device_mac: 'device_mac',
			hostname: 'hostname',
			device_type: 'device_type',
			sort: 'sort',
			site: 'site',
			serial: 'serial',
			level: 'level',
			event_description: 'event_description',
			event_type: 'event_type',
			event_number: 'event_number',
			event_category: 'event_category',
			fields: 'fields',
			calculate_total: 'calculate_total',
		};

		// Process all additional fields and map to query parameters
		for (const [key, apiParam] of Object.entries(paramMappings)) {
			if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
				qs[apiParam] = additionalFields[key];
				logger.debug(`parameter:${key}`, `Setting ${apiParam} to ${additionalFields[key]}`);
			}
		}

		// Construct API endpoint
		const endpoint = '/monitoring/v2/events';

		logger.debug(
			'api:request:prepared',
			`Endpoint: ${endpoint}, Query params: ${JSON.stringify(qs)}`,
		);

		// Handle pagination
		if (returnAll) {
			const responseData: IDataObject[] = await apiRequestAllItems.call(
				this,
				'GET',
				endpoint,
				{},
				qs,
			);
			return [{ json: responseData }];
		}

		// Make API request
		const responseData = await apiRequest.call(this, 'GET', endpoint, {}, qs);

		// Format and return response
		return [{ json: responseData }];
	} catch (error) {
		logger.error('monitoring:events:getEvents:error', { message: error.message });
		return handleApiError.call(this, error, 'Failed to execute get events');
	}
}

/**
 * Helper for pagination to fetch all items from a paginated API endpoint
 *
 * @param this The n8n execution context
 * @param method HTTP method to use
 * @param endpoint API endpoint to call
 * @param body Request body (if applicable)
 * @param qs Query parameters
 * @returns Combined array of all items across pages
 */
async function apiRequestAllItems(
	this: IExecuteFunctions,
	method: string,
	endpoint: string,
	body: IDataObject = {},
	qs: IDataObject = {},
): Promise<IDataObject[]> {
	const returnData: IDataObject[] = [];
	let responseData;

	const requestLimit = 100; // API limit per request
	let hasMore = true;
	let offset = 0;

	// Initialize or use the existing limit
	const queryLimit = qs.limit ? Math.min(qs.limit as number, requestLimit) : requestLimit;
	qs.limit = queryLimit;

	logger.debug('pagination:start', `Starting pagination with limit ${queryLimit}`);

	do {
		qs.offset = offset;
		logger.debug('pagination:request', `Making request with offset=${offset}, limit=${queryLimit}`);

		responseData = await apiRequest.call(this, method, endpoint, body, qs);
		logger.debug('pagination:response', 'Response received');

		// This needs to be adjusted based on actual API response format
		const items = responseData.items || responseData.data || responseData;

		if (items && Array.isArray(items)) {
			logger.debug('pagination:items', `Received ${items.length} items`);
			returnData.push(...items);

			// If we received fewer items than requested, assume no more items
			if (items.length < queryLimit) {
				logger.debug(
					'pagination:complete',
					'Received fewer items than requested, assuming no more items',
				);
				hasMore = false;
			} else {
				// Prepare for next batch
				offset += items.length;
				logger.debug('pagination:next', `Moving to next page, new offset: ${offset}`);
			}
		} else {
			// No items in response or not an array
			logger.debug('pagination:complete', 'No items found in response or response is not an array');
			if (items) {
				returnData.push(items as IDataObject);
			}
			hasMore = false;
		}
	} while (hasMore);

	logger.debug(
		'pagination:complete',
		`Pagination complete. Total items retrieved: ${returnData.length}`,
	);
	return returnData;
}
