import { onDebug } from '@atlassian/clientside-extensions-debug';
import { useCallback, useRef } from 'react';

const copyAttributes = ['type', 'src', 'nonce', 'noModule'] as const;

const evaluateScripts = (originalScript: HTMLScriptElement) => {
    const script = document.createElement('script');
    script.text = originalScript.text;
    copyAttributes.forEach((attr) => {
        // we cant just check if an attribute exists, as some of them are enforced to be "properties"
        // see: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce
        if (originalScript[attr]) {
            // @ts-expect-error We want to assign whatever here, typescript be quiet!
            script[attr] = originalScript[attr]!;
        }
    });

    try {
        document.head.appendChild(script);
    } catch (e) {
        onDebug(({ error }) => ({
            level: error,
            message: `An inline script in a WebPanel failed to execute.`,
            meta: {
                error: e,
            },
        }));
    }

    try {
        script.remove();
    } catch (e) {
        onDebug(({ error }) => ({
            level: error,
            message: `Failed to remove inline script associated with WebPanel.`,
            meta: {
                error: e,
            },
        }));
    }
};

const injectAndEvaluateInlineScripts = (node: HTMLElement, html: string) => {
    const template = document.createElement('template');
    template.innerHTML = html;
    const scripts = template.content.querySelectorAll('script');
    scripts.forEach((script) => script.remove());

    node.appendChild(template.content);

    scripts.forEach((script) => evaluateScripts(script));
};

type WebpanelRootNode = { __cse_webpanel_initialized: boolean } & HTMLElement;

const useWebPanelRenderer = (html: string) => {
    const ref = useRef<WebpanelRootNode>(null);
    const setRef = useCallback<(node: HTMLElement | null) => void>(
        (node) => {
            // eslint-disable-next-line no-underscore-dangle
            if (ref?.current?.__cse_webpanel_initialized) {
                return;
            }

            // where the actual work happens.
            if (node) {
                injectAndEvaluateInlineScripts(node, html);
                // eslint-disable-next-line no-underscore-dangle
                (node as WebpanelRootNode).__cse_webpanel_initialized = true;
            }

            // @ts-expect-error This is typed as a "read-only" property, but in fact it can be set.
            ref.current = node;
        },
        [html],
    );

    return setRef;
};

export default useWebPanelRenderer;
