import {
	CreateSourceConnectionPayload,
	DeleteSourcePayload,
	GetParserPayload,
	IntegrationRecord,
	Integrations,
	ListSourcesPayload,
	SourcePlatforms,
	SourcePlatformsForSetEvents,
	SubscribeSourceEventPayload,
} from '@event-inc/types';
import {
	BError,
	checkIfKeyOrIdExists,
	getIntegrationDefinitionId,
	handleBuildableError,
	makeHttpNetworkCall,
	matchResultAndHandleHttpError,
} from '@event-inc/utils';
import { identity } from 'ramda';
import { getDefaultParser } from '../logic/parser';
import { version1 } from './configs.apis';

export const createSourceConnectionApi = <T extends SourcePlatforms>(
	headers: Record<string, string>,
	url: string,
	payload: CreateSourceConnectionPayload<T>
) => {
	const authFormData: {
		name: string
	} | {
		name: string
		[key: string]: string
	} = payload.type === 'webhook' || 
			payload.type === 'nodejs' || 
			payload.type === 'rutter' ? {
		name: payload.label,
	} : {
		...payload.config,
		name: payload.label,
	};

	const integrationDefinitionId = getIntegrationDefinitionId(
		payload.type,
		'source'
	);

	let updatedAuthFormData = authFormData as {
		[key: string]: string
	};

	if (payload.type === 'ftp') {
		const { FTP_PARSER } = payload.config;
		const stringParser = FTP_PARSER as string;

		if (["csv", "json"].includes(stringParser)) {
			updatedAuthFormData.FTP_EXTRACTOR_FILE_PARSER = getDefaultParser(stringParser);
		} else {
			updatedAuthFormData.FTP_EXTRACTOR_FILE_PARSER = FTP_PARSER.toString();
		}
	}

	const finalPayload = {
		integrationDefinitionId,
		authFormData: updatedAuthFormData,
		type: payload.type,
		group: payload.group,
		name: payload.label,
		key: payload.key,
	};

	return makeHttpNetworkCall<IntegrationRecord>({
		method: 'POST',
		url: `${url}/${version1}/integrations/create`,
		data: finalPayload,
		headers,
	});
};

export const deleteSourceConnectionApi = (
	headers: Record<string, string>,
	url: string,
	payload: DeleteSourcePayload
) => {
	const { key, id } = payload;

	checkIfKeyOrIdExists(
		id,
		key,
		'Must provide either id or key to delete a source'
	);

	return makeHttpNetworkCall({
		method: 'POST',
		url: `${url}/${version1}/integrations/delete`,
		data: {
			key,
			integrationId: id,
		},
		headers,
	});
};

export const subscribeEventsApi = async <T extends SourcePlatformsForSetEvents>(
	headers: Record<string, string>,
	url: string,
	payload: SubscribeSourceEventPayload<T>
) => {
	const { events, type, key, id } = payload;

	checkIfKeyOrIdExists(
		id,
		key,
		'Must provide either id or key to set events for a source'
	);

	if (type === 'nodejs') {
		const eventList = events.join(',');
		return makeHttpNetworkCall({
			method: 'POST',
			url: `${url}/${version1}/integrations/add-custom-events`,
			data: {
				key,
				id,
				events: eventList,
			},
			headers,
		});
	}

	const result = await makeHttpNetworkCall({
		method: 'POST',
		url: `${url}/${version1}/integrations/update-subscribed-events`,
		data: {
			events,
			key,
			integrationId: id,
		},
		headers,
	});

	const listIntegration = async () => {
		const response = await makeHttpNetworkCall<Integrations>({
			method: 'POST',
			url: `${url}/${version1}/integrations/list`,
			headers,
			data: {
				query: {
					_id: id,
					key,
				},
			},
		});

		const { data } = matchResultAndHandleHttpError(response, identity);

		return data;
	};

	const poll = async function (startTime: number, timeout: number) {
		let result = await listIntegration();

		while (
			['pending', 'processing'].includes(
				result?.rows?.[0]?.states?.webhooks?.all?.state
			)
		) {
			if (Date.now() - startTime > timeout) {
				handleBuildableError(
					BError(
						false,
						'http_408',
						'Connection timed out',
						'client-destination',
						false
					)
				);
			}

			await wait();
			result = await listIntegration();
		}

		if (result?.rows?.[0]?.states?.webhooks?.all?.state !== 'complete') {
			handleBuildableError(
				BError(
					false,
					'http_500',
					result?.rows?.[0]?.states?.webhooks?.all?.message,
					'client-destination',
					false
				)
			);
		}

		return result;
	};

	const wait = function (ms = 2000) {
		return new Promise((resolve) => {
			setTimeout(resolve, ms);
		});
	};

	const startTime = Date.now();
	const timeout = 60000;
	await poll(startTime, timeout);

	return result;
};

export const listSourcesApi = (
	headers: Record<string, string>,
	url: string,
	payload: ListSourcesPayload
) => {
	return makeHttpNetworkCall<Integrations>({
		method: 'POST',
		url: `${url}/${version1}/integrations/list`,
		data: {
			...payload,
			query: {
				...payload?.query,
				_id: {
					$regex: 'conn_src',
				},
			},
		},
		headers,
	});
};

export const getParserApi = async (
	headers: Record<string, string>,
	url: string,
	payload: GetParserPayload
): Promise<string> => {
	const { id, key } = payload;

	const response = await makeHttpNetworkCall<Integrations>({
		method: 'POST',
		url: `${url}/${version1}/integrations/list`,
		data: {
			query: {
				_id: id,
				key,
				type: 'ftp',
			},
			fields: ["parser"]
		},
		headers,
	});

	const { data } = matchResultAndHandleHttpError(response, identity);

	if (data.rows.length > 0) {
		return data.rows[0].parser || getDefaultParser("csv");
	}
	
	return "Not found";
};
