import React, { ReactElement, ReactNode, useEffect, useRef } from 'react';

import { useFetch } from '@divi/rest';

interface ServerSideRenderProps {
	block: string;
	attributes: object;
	queryArgs?: object;
	fallback?: ReactNode;
	isEmptyResponse?: (html: string) => boolean;
}

declare global {
	interface Window {
		JetEngineDiviViews: any;
		JetEngine?: {
			initBlocks?: ( scope?: JQuery<HTMLElement> ) => void;
		};
		jQuery?: any;
	}
}

/**
 * Static Module edit component of visual builder.
 *
 * @since ??
 *
 * @param {DynamicFieldEditProps} props React component props.
 *
 * @returns {ReactElement}
 */
const ServerSideRender: React.FC<ServerSideRenderProps> = ( props ) => {

	const {
		fetch,
		response,
		isLoading,
	} = useFetch<any>({});

	const {
		block,
		attributes,
		queryArgs,
		fallback,
		isEmptyResponse,
	} = props;

	const fetchAbortRef = useRef<AbortController>();
	const renderedRef   = useRef<HTMLDivElement>( null );

	const buildQueryString = function( params: { [key: string]: any }, parentKey = '' ):string {

		const queryParts = [];

		for ( const [ key, value ] of Object.entries( params ) ) {

				const fullKey = parentKey ? `${parentKey}[${key}]` : key;

				if ( typeof value === 'object' && value !== null ) {
						queryParts.push( buildQueryString( value, fullKey ) );
				} else {
						queryParts.push( `${encodeURIComponent(fullKey)}=${encodeURIComponent(value)}` );
				}
		}

		return queryParts.join( '&' );
	}

	useEffect(() => {

		if( fetchAbortRef.current ) {
			fetchAbortRef.current.abort();
		}

		fetchAbortRef.current = new AbortController();

		let fullQueryArgs = {
			is_jet_block_editor_render: true,
			context: 'edit',
			_locale: 'user',
			object: window?.JetEngineDiviViews?.object_id,
			listing: window?.JetEngineDiviViews?.settings,
			ts: Date.now(),
		};

		if ( queryArgs ) {
			fullQueryArgs = { ...fullQueryArgs, ...queryArgs };
		}

		let queryString = buildQueryString( fullQueryArgs );

		fetch({
			restRoute: `/wp/v2/block-renderer/${block}?${queryString}`,
			method:    'POST',
			body:      JSON.stringify( { attributes: attributes || {}, } ),
			signal:    fetchAbortRef.current.signal,
		}).
		catch((error) => {
			console.error(error);
		});

		return () => {
			if(fetchAbortRef.current) {
				fetchAbortRef.current.abort();
			}
		};
	}, [attributes]);

	useEffect( () => {

		if ( isLoading || ! response?.rendered || ! renderedRef.current ) {
			return;
		}

		// JetEngine block callbacks are bound via JetPlugins.init and should be
		// re-run after each SSR HTML update inside Divi editor.
		if ( window?.JetEngine?.initBlocks && window?.jQuery ) {
			window.JetEngine.initBlocks( window.jQuery( renderedRef.current ) );
		}
	}, [ isLoading, response?.rendered ] );

	const hasRenderedMarkup = !! response?.rendered
		&& response.rendered.trim() !== ''
		&& ! isEmptyResponse?.( response.rendered );

	return ( <>
		{ ! isLoading && hasRenderedMarkup && (
			<div ref={ renderedRef } dangerouslySetInnerHTML={ { __html: response.rendered } }></div>
		) }
		{ ! isLoading && ! hasRenderedMarkup && fallback }
		{ isLoading && (
			<div>Loading ...</div>
		) }
		</>
	);
}

export default ServerSideRender;
