//React
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

//System
import { Wrapper } from '../../../system/wrapper/wrapper';

//Custom hooks
import usePortalContainer from '../../../system/customHooks/usePortalContainer';

//Plugins
import { useFloating } from '@floating-ui/react';

//Events

//This event waits for the popover entry to be properly mounted, after which it attaches a listener
// to the element that reacts whenever the element changes size. The reason we need to do this is
// because popper doesn't know when wrapper has finished rendering. In simple situations, popper
// would be rendered as static HTML elements but we render dynamic JSON through wrapper and as a
// result, this unfortunate hack is needed. In the future, we might be able to bubble up 'onMount'
// events and deduce when a component is done rendering (because all its children are rendered).
const onFixPopperPosition = (container, id, update) => {
	if (!container || !update)
		return;

	(async () => {
		//We can't use #id because id's could start with numbers, which makes the selector invalid.
		const el = container.querySelector(`[id='${id}']`);
		if (!el) {
			requestAnimationFrame(onFixPopperPosition.bind(null, container, id, update));

			return;
		}

		(new ResizeObserver(update)).observe(el);
	})();
};

//Components

//This is the last component in the 'chain'. It finally renders the actual popoverMda and
// uses the popoverRef for positioning purposes (through the popper library)
const PopoverEntry = ({ mda, popoverRef }) => {
	const { position, popoverContainer = 'POPOVERS', popoverZIndex = 1, pointerEvents } = mda;

	const renderInEl = usePortalContainer(popoverContainer);

	const { refs, floatingStyles, update } = useFloating({
		elements: {
			reference: popoverRef,
		},
		placement: position
	});

	useEffect(onFixPopperPosition.bind(null, renderInEl, mda.id, update), [renderInEl, update]);

	const style = {
		...floatingStyles,
		zIndex: popoverZIndex
	};

	//When the mda opts in (tooltips do, via buildTooltip), the rendered popover must not be a
	// hit-test target, so a popover that overlaps the cursor can't steal the pointer from its
	// owner and cause a hover flicker loop in Chrome (PF-6911). Interactive popovers omit this.
	if (pointerEvents)
		style.pointerEvents = pointerEvents;

	//Popover metadata (e.g. tooltipMda) is rendered here. In a transpiled app its `type` is the
	// component *function* (e.g. ContainerSimple) rather than a registry string; Wrapper only resolves
	// string types, so render a function type directly — the component itself builds a string-typed
	// Wrapper internally. String types keep going through Wrapper as before.
	const Content = mda.type;
	const content = (typeof Content === 'function')
		? (
			<Content
				id={mda.id}
				scope={mda.scope}
				relId={mda.relId}
				parentId={mda.parentId}
				prps={mda.prps}
				wgts={mda.wgts}
			/>
		)
		: <Wrapper mda={mda} />;

	const el = (
		<div
			ref={refs.setFloating}
			style={style}
		>
			{content}
		</div>
	);
	if (!renderInEl)
		return null;

	const res = ReactDOM.createPortal(el, renderInEl);

	return res;
};

export default PopoverEntry;
