import { TEvaluateSourceFactory } from '../types';

export const createEvaluateSource: TEvaluateSourceFactory = (window) => {
    return (source) =>
        new Promise((resolve, reject) => {
            if (window === null) {
                // Bug #182 Chrome and Edge do throw an instance of a SyntaxError instead of a DOMException.
                reject(new SyntaxError());

                return;
            }

            const head = window.document.head;

            if (head === null) {
                // Bug #182 Chrome and Edge do throw an instance of a SyntaxError instead of a DOMException.
                reject(new SyntaxError());
            } else {
                const script = window.document.createElement('script');
                // @todo Safari doesn't like URLs with a type of 'application/javascript; charset=utf-8'.
                const blob = new Blob([source], { type: 'application/javascript' });
                const url = URL.createObjectURL(blob);

                const originalOnErrorHandler = window.onerror;

                const removeErrorEventListenerAndRevokeUrl = () => {
                    window.onerror = originalOnErrorHandler;

                    URL.revokeObjectURL(url);
                };

                window.onerror = (message, src, lineno, colno, error) => {
                    // @todo Edge thinks the source is the one of the html document.
                    if (src === url || (src === window.location.href && lineno === 1 && colno === 1)) {
                        removeErrorEventListenerAndRevokeUrl();
                        reject(error);

                        return false;
                    }

                    if (originalOnErrorHandler !== null) {
                        return originalOnErrorHandler(message, src, lineno, colno, error);
                    }
                };

                script.onerror = () => {
                    removeErrorEventListenerAndRevokeUrl();
                    // Bug #182 Chrome and Edge do throw an instance of a SyntaxError instead of a DOMException.
                    reject(new SyntaxError());
                };
                script.onload = () => {
                    removeErrorEventListenerAndRevokeUrl();
                    resolve();
                };
                script.src = url;
                script.type = 'module';

                head.appendChild(script);
            }
        });
};
