import React from "react";
import {createRoot, type Root} from "react-dom/client";
import {ErrorBoundary} from "react-error-boundary";
import {__} from "@wordpress/i18n";

declare let elementorFrontend: any;
const rootMap = new Map<HTMLDivElement, Root>();

abstract class FieldBase {
	private readonly name: string;

	/**
	 * Unique field component
	 * @param props
	 */
	abstract fieldComponent(props: any): React.JSX.Element;

	/**
	 * Error Boundary fallback
	 * @param root0
	 * @param root0.error
	 * @protected
	 */
	protected fallbackRender({error}: any) {
		return (
			<div role="alert" className={'hulk-error-boundary'}>
				<pre style={{color: "red"}}>
					{__('Something went wrong:', 'advanced-fields-for-elementor-forms')}
					<br/>
					{error.message}
				</pre>
			</div>
		);
	}

	/**
	 * Render field
	 * @param element
	 * @protected
	 */
	protected handleField(element: HTMLDivElement) {
		const settingsKey = element.getAttribute('data-key') as string;
		const settingsElement = document.getElementById(settingsKey) as HTMLScriptElement;
		const settings = JSON.parse(settingsElement.textContent as string);
		if (!rootMap.has(element)) {
			rootMap.set(element, createRoot(element));
		}
		const DynamicComponent = this.fieldComponent(settings);
		// @ts-ignore
		if (process.env.NODE_ENV !== 'production') {
			rootMap.get(element)?.render(
				<ErrorBoundary fallbackRender={this.fallbackRender.bind(this)}>
					<React.StrictMode>{DynamicComponent}</React.StrictMode>
				</ErrorBoundary>
			);
		} else {
			rootMap.get(element)?.render(
				<ErrorBoundary fallbackRender={this.fallbackRender.bind(this)}>
					{DynamicComponent}
				</ErrorBoundary>
			);
		}
	}

	/**
	 * Handle form widget
	 */
	protected handleWidget() {
		const formElements = document.querySelectorAll('.elementor-form') as NodeListOf<HTMLFormElement>;
		formElements.forEach((formElement: HTMLFormElement) => {
			const fields = formElement.querySelectorAll(`.${this.name}_wrap`) as NodeListOf<HTMLDivElement>;
			fields.forEach((element: HTMLDivElement) => this.handleField(element));
		});
	}

	/**
	 * Form widget init
	 * @protected
	 */
	protected onInit() {
		const formElements = document.querySelectorAll('.elementor-form');
		if (formElements.length > 0) {
			this.handleWidget.bind(this)();
		} else {
			elementorFrontend.hooks.addAction('frontend/element_ready/form.default', this.handleWidget.bind(this));
		}
	}

	/**
	 * Instance
	 * @param name Unique field name
	 */
	constructor(name: string) {
		this.name = name;
		window.addEventListener('elementor/frontend/init', this.onInit.bind(this));
	}
}

export default FieldBase;
