import type { FunctionComponent, MutableRefObject } from 'react';
import React, { useEffect, useMemo, useRef } from 'react';
import type { Context } from '@atlassian/clientside-extensions-registry';
import { PanelExtension } from '@atlassian/clientside-extensions';

export interface PanelHandlerProps {
    render: PanelExtension.PanelRenderExtension;
    RootType?: 'div' | 'span';
    /*
        Hello dear reader of inline comments. You might rightfully ask: what the hell is "context" doing here.
        Well turns out you are not the first.
        The reason is, that async-panel is reusing the panel handler under the hood. And the only way to let the panel have
        access to the context is by passing it through via a "contextProvider" (check the AsyncPanelExtension).
        So yeah, that is why we are here. And that is why "context" has to stay.
     */
    context?: Context<{}>;
}

const isDiv = (RooType: string): RooType is 'div' => RooType === 'div';

export const PanelHandler: FunctionComponent<PanelHandlerProps> = ({ render, RootType = 'div', context }) => {
    const panelApi = useMemo(() => new PanelExtension.Api(), []);
    const ref = useRef<HTMLDivElement | HTMLSpanElement>(null);

    useEffect(
        function callMountAndUnmountLifecycleCallbacks() {
            const node = ref.current;

            if (render) {
                render(panelApi, context);
            }

            if (node) {
                panelApi.getRenderCallback()(node);
            }

            return () => {
                if (node) {
                    panelApi.getCleanupCallback()(node);
                }
            };
        },
        [panelApi, render, context],
    );

    if (isDiv(RootType)) {
        return <RootType ref={ref as MutableRefObject<HTMLDivElement>} />;
    }

    return <RootType ref={ref as MutableRefObject<HTMLSpanElement>} />;
};
