import { getTokensAPIURL } from '../service/api';
import { post } from '../service/api';
import {
	CardCollectProps,
	CardCollectResponse,
	GenerateIFrameFieldProps,
	IFrameValuesPostMessageResponse
} from '../types/types';
import { validateFields, generateError } from '../utils/validations';

const handleCardCollectV2 = ({
	onCardCollectFrameLoaded,
	onFieldChange,
	onDCCData,
	pciFieldsBasePath,
	dccUrl,
	validateOnFrame,
	i18nMessages,
	displayErrors,
	css,
	env = 'production'
}: CardCollectProps = {}): CardCollectResponse => {
	const PAYBYRD_API_TOKEN_URL = getTokensAPIURL(env);

	const cHolder = document.getElementById('cc-holder');
	const cNumber = document.getElementById('cc-number');
	const cExpDate = document.getElementById('cc-expiration-date');
	const cCVV = document.getElementById('cc-cvc');

	const handleMessage = (event: MessageEvent) => {
		if (event.data.type === 'PB_PCI_FIELD_CHANGE') {
			onFieldChange?.(event.data);
		}

		if (event.data.type === 'PB_PCI_DCC_DATA') {
			onDCCData?.(event.data);
		}
	};

	window.addEventListener('message', handleMessage);

	const destroy = () => {
		window.removeEventListener('message', handleMessage);
	};

	const fieldsToLoad = [
		cHolder ? 'cHolder' : null,
		cNumber ? 'cNumber' : null,
		cExpDate ? 'cExpDate' : null,
		cCVV ? 'cCVV' : null
	].filter(Boolean).length;

	let loadedFields = 0;

	const generateIFrameField = ({
		src,
		placeholder,
		wrapper,
		id,
		css
	}: GenerateIFrameFieldProps) => {
		const iframeField = document.createElement('iframe');
		iframeField.src = src;
		iframeField.id = id || '';
		iframeField.classList.add('pb-secure-field');
		iframeField.style.border = '0';
		iframeField.style.width = '100%';
		iframeField.style.height = '100%';

		wrapper.append(iframeField);

		iframeField.onload = () => {
			loadedFields++;

			iframeField.contentWindow?.postMessage(
				{ type: 'PB_PCI_METADATA', data: { placeholder, css, dccUrl } },
				'*'
			);

			if (loadedFields === fieldsToLoad) {
				onCardCollectFrameLoaded?.();
			}
		};
	};

	// Generate fields in DOM
	if (cHolder) {
		generateIFrameField({
			id: 'cc-holder',
			src: `${pciFieldsBasePath}/pci-card-holder.html`,
			placeholder: i18nMessages?.holderName || 'Card Holder',
			wrapper: cHolder,
			css
		});
	}
	if (cNumber) {
		generateIFrameField({
			id: 'cc-number',
			src: `${pciFieldsBasePath}/pci-card-number.html`,
			placeholder: i18nMessages?.cardNumber || 'Card Number',
			wrapper: cNumber,
			css
		});
	}
	if (cExpDate) {
		generateIFrameField({
			id: 'cc-expiration-date',
			src: `${pciFieldsBasePath}/pci-card-exp-date.html`,
			placeholder: i18nMessages?.expDate || 'MM/YY',
			wrapper: cExpDate,
			css
		});
	}
	if (cCVV) {
		generateIFrameField({
			id: 'cc-cvc',
			src: `${pciFieldsBasePath}/pci-card-cvv.html`,
			placeholder: i18nMessages?.cvv || 'CVV',
			wrapper: cCVV,
			css
		});
	}

	const getIFrameValues = async (): Promise<IFrameValuesPostMessageResponse> => {
		const iframes = document.querySelectorAll('.pb-secure-field');
		const fieldData = {} as IFrameValuesPostMessageResponse;

		// Get All Values from each iFrame field before sending to Paybyrd's Payments API
		async function getFieldData(iframe: HTMLIFrameElement) {
			return new Promise((resolve) => {
				const messageListener = (event: MessageEvent) => {
					if (event.data.type === 'PB_PCI_FIELD_VALUE') {
						window.removeEventListener('message', messageListener);
						resolve({ [event.data.field as string]: event.data.value });
					}
				};

				window.addEventListener('message', messageListener);
				iframe.contentWindow?.postMessage(
					{ type: 'PB_PCI_GET_VALUES', data: { id: iframe.id } },
					'*'
				);
			});
		}

		for (const iframe of Array.from(iframes)) {
			Object.assign(fieldData, await getFieldData(iframe as HTMLIFrameElement));
		}

		return fieldData;
	};

	const clearIFrameErrors = () => {
		const iframes = document.querySelectorAll('.pb-secure-field');

		for (const iframe of Array.from(iframes)) {
			(iframe as HTMLIFrameElement).contentWindow?.postMessage(
				{ type: 'PB_PCI_CLEAR_ERROR' },
				'*'
			);
		}
	};

	const generateIFrameErrors = (
		field: HTMLElement,
		errorData: Record<string, string>,
		validateOnFrame?: boolean
	) => {
		field
			.querySelector('iframe')
			?.contentWindow?.postMessage(
				{ type: 'PB_PCI_FIELD_ERROR', data: { errorData, validateOnFrame } },
				'*'
			);
	};

	const submit = async () => {
		clearIFrameErrors();
		const fields = await getIFrameValues();
		let normalizedExpDate = fields['cc-expiration-date'];

		if (normalizedExpDate && /^\d{3,4}$/.test(normalizedExpDate)) {
			const formattedDate = normalizedExpDate.padStart(4, '0');
			normalizedExpDate = `${formattedDate.slice(0, 2)}/${formattedDate.slice(2)}`;
		}

		const { isValid, errors } = validateFields({
			holderValue: fields['cc-holder'],
			cardValue: fields['cc-number'],
			dateValue: normalizedExpDate,
			cvvValue: fields['cc-cvc'],
			i18nMessages
		});

		if (!isValid) {
			Object.entries(errors).map((error) => {
				const field = document.getElementById(error[0]);
				const errorData = error[1];

				if (field) {
					generateError({
						field,
						displayErrors,
						errorData
					});

					generateIFrameErrors(field, errorData, validateOnFrame);
				}
			});

			return Promise.reject(errors);
		}

		// Returns tokenized card data to fetch /payment
		return post(`${PAYBYRD_API_TOKEN_URL}/api/v1/tokens`, {
			holder: fields['cc-holder'] || '',
			number: fields['cc-number'] ? fields['cc-number'].replace(/ /g, '') : '',
			expiration: fields['cc-expiration-date'] || '',
			cvv: fields['cc-cvc'] || ''
		}).then((response) => {
			return {
				status: 200,
				data: response
			};
		});
	};

	return { cardCollect_submit: submit, destroy };
};

export default handleCardCollectV2;
