import {startTransition, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import renderHtmlAttributes from "../../utils/renderHtmlAttributes";
import extractFieldId from "../../utils/extractFieldId";
import dateDiffInDays from "../../utils/dateDiffInDays";
import evaluateMathExpression from "../../utils/evaluateMathExpression";
import dateDiffInYears from "../../utils/dateDiffInYears";
import dateDiffInMonths from "../../utils/dateDiffInMonths";
import dateDiffInWeeks from "../../utils/dateDiffInWeeks";
import addDaysToDate from "../../utils/addDaysToDate";
import subtractDaysFromDate from "../../utils/subtractDaysFromDate";
import formatDate from "../../utils/formatDate";
import numberFormat from "../../utils/numberFormat";
import type {FreemiusProps} from "../../types/freemius";
import UpgradeOverlay from "../../components/UpgradeOverlay";
import useFormSubmitSuccess from '../../hooks/useFormSubmitSuccess';

type FieldProps = {
	itemId: string
	formId: string
	attrs: Record<string, string | string[]>
	formula: string
	listenFields: string[]
	prefix: string
	suffix: string
	isHidden: boolean
	numberFormat: boolean
	decimalPoints: number
	decimalSeparator: string
	thousandSeparator: string
} & FreemiusProps

const Field = (props: FieldProps) => {

	const inputRef = useRef<HTMLInputElement>(null);
	const attrs = useMemo(() => renderHtmlAttributes(props.attrs), [props.attrs]);

	const [value, setValue] = useState(0);
	const [values, setValues] = useState<{ [key: string]: string }>({});

	// Reset after form submit
	useFormSubmitSuccess(inputRef, () => {
		const form = inputRef.current?.closest('.elementor-form');
		if (!form) {
			return;
		}
		setTimeout(function () {
			props.listenFields.forEach((id) => {
				let elements = form.querySelectorAll(`[name="form_fields[${id}]"]`);
				if (!elements.length) {
					const elements2 = form.querySelectorAll(`[name="form_fields[${id}][]"]`);
					if (!elements2.length) {
						return;
					}
					elements = elements2;
				}
				elements.forEach((el) => {
					el.dispatchEvent(new Event('change', {bubbles: true}));
					el.dispatchEvent(new Event('input', {bubbles: true}));
				});
			});
		}, 400);
	}, []);

	const listenFieldsObject = useMemo(() =>
		props.listenFields.reduce<Record<string, number>>((acc, key) => {
			acc[key] = 0;
			return acc;
		}, {}), [props.listenFields]
	);

	const handleInputChange = useCallback((e: any) => {
		const {name, value: val} = e.target;
		const id = extractFieldId(name);
		if (!id) {
			return;
		}
		let trimVal = String(val).trim();
		if (e.target.classList.contains('elementor-date-field')) {
			const date = Date.parse(trimVal);
			trimVal = isNaN(date) ? '0' : date.toString();
		} else if (e.target.type === 'radio' && !e.target.checked) {
			trimVal = '0';
		} else if (e.target.type === 'checkbox') {
			const checkboxes = e.target
				.closest('.elementor-field-type-checkbox')
				.querySelectorAll('input[type="checkbox"]');
			const checked = Array
				.from<HTMLInputElement>(checkboxes)
				.filter((checkbox: HTMLInputElement) => checkbox.checked);
			const checkedVal = checked.map((checkbox: HTMLInputElement) => checkbox.value);
			trimVal = checkedVal.join('+');
		} else if (e.target.classList.contains('irs-hidden-input')) {
			trimVal = trimVal.split(',').join('+');
		} else if (e.target.classList.contains('hulk_affef_image_choices_field')) {
			trimVal = trimVal.split(',').join('+');
		}
		setValues(prevState => ({...prevState, [id]: trimVal}));
	}, []);

	useEffect(() => {
		const fields: Element[] = [];
		setTimeout(function () {
			const form = inputRef.current?.closest('.elementor-form');
			if (!form) {
				return;
			}
			if (!props.freemius.can_use_premium_code) {
				return;
			}
			props.listenFields.forEach((id) => {
				let elements = form.querySelectorAll(`[name="form_fields[${id}]"]`);
				if (!elements.length) {
					const elements2 = form.querySelectorAll(`[name="form_fields[${id}][]"]`);
					if (!elements2.length) {
						return;
					}
					elements = elements2;
				}
				elements.forEach((el) => {
					fields.push(el);
					el.addEventListener("change", handleInputChange);
					el.addEventListener("input", handleInputChange);
					el.dispatchEvent(new Event('change', {bubbles: true}));
					el.dispatchEvent(new Event('input', {bubbles: true}));
				});
			});
		}, 400);
		// Cleanup
		return () => {
			fields.forEach((input) => {
				input.removeEventListener("change", handleInputChange);
				input.removeEventListener("input", handleInputChange);
			});
		};
	}, [handleInputChange, props.freemius.can_use_premium_code, props.listenFields]);

	useEffect(() => {
		startTransition(() => {
			// Function to replace placeholders with actual values
			const updatePlaceholder = (template: string, data: { [key: string]: string }) => {
				return template.replace(/[a-zA-Z0-9-_]+/g, (key) => data[key] || key);
			};
			// Regular expression to match unquoted placeholders
			const updatedFormula = props.formula.replace(/(,\s*)([A-Za-z0-9_-]+)(\s*\))/g, '$1"$2"$3');
			const updatedString = updatePlaceholder(updatedFormula, values);
			setValue(evaluateMathExpression(updatedString, {
				dateDiffInDays,
				dateDiffInYears,
				dateDiffInMonths,
				dateDiffInWeeks,
				addDaysToDate,
				subtractDaysFromDate,
				formatDate,
				...listenFieldsObject
			}));
		});
	}, [listenFieldsObject, props.formula, props.listenFields, values]);

	const newVal = useMemo(() => {
		if (props.numberFormat) {
			return numberFormat(
				value,
				props.decimalPoints,
				props.decimalSeparator,
				props.thousandSeparator
			);
		}
		return value;
	}, [props.decimalPoints, props.decimalSeparator, props.numberFormat, props.thousandSeparator, value]);

	return (
		<>
			{!props.freemius.can_use_premium_code && <UpgradeOverlay url={props.freemius.get_upgrade_url}/>}
			<input ref={inputRef} type="hidden" {...attrs} defaultValue={newVal}/>
			{!props.isHidden && (
				<div className="hulk-calculated-value">
					{props.prefix && <span className="hulk-prefix">{props.prefix}</span>}
					<span className={'hulk-val'}>{newVal}</span>
					{props.suffix && <span className="hulk-suffix">{props.suffix}</span>}
				</div>
			)}
		</>
	);
};

export default Field;
