'use strict';

import {extend} from "../../source/data/extend.mjs";
import {getGlobal} from "../../source/types/global.mjs";
import {ResizeObserverMock} from "./resize-observer.mjs";

export const isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
export const isNode = new Function("try {return this===global;}catch(e){return false;}");

let JSDOMExport = null;

/**
 * this helper function creates the dom stack in the node environment
 *
 * @return {Promise<unknown>|Promise<void>}
 */
function initJSDOM(options) {
    if (typeof window === "object" && window['DOMParser']) return Promise.resolve();

    const g = getGlobal();

    options = extend({}, {
        pretendToBeVisual: true,
        contentType: "text/html",
        includeNodeLocations: true,
        storageQuota: 10000000,
        runScripts: "dangerously",
        resources: "usable",
        url: "https://example.test/"
    }, options || {})

    return import("jsdom").then(({JSDOM}) => {
        JSDOMExport = JSDOM;
        const {window} = new JSDOM(`<!DOCTYPE html><html lang="en"><head><title>Test</title></head><body><div id="mocks"></div></body></html>`, options);

        g['window'] = window;

        return new Promise((resolve, reject) => {
            let settled = false;

            const finalize = () => {
                if (settled) return;
                settled = true;

                [
                    'Blob',
                    'CSSStyleSheet',
                    'customElements',
                    'CustomEvent',
                    'cancelAnimationFrame',
                    'document',
                    'DOMException',
                    'DOMImplementation',
                    'Document',
                    'DocumentFragment',
                    'DOMParser',
                    'Element',
                    'ElementInternals',
                    'Event',
                    'EventTarget',
                    'getComputedStyle',
                    'HTMLButtonElement',
                    'HTMLCollection',
                    'HTMLDivElement',
                    'HTMLDocument',
                    'HTMLElement',
                    'HTMLFormElement',
                    'HTMLInputElement',
                    'HTMLScriptElement',
                    'requestAnimationFrame',
                    'HTMLSelectElement',
                    'HTMLTemplateElement',
                    'HTMLTextAreaElement',
                    'InputEvent',
                    'KeyboardEvent',
                    'MutationObserver',
                //    'navigator',
                    'Node',
                    'NodeFilter',
                    'NodeList',
                    'self',
                    'ShadowRoot',
                    'XMLSerializer',
                ].forEach(key => {
                    try {
                        g[key] = window[key]
                    } catch(e) {
                        console.error("Error setting key", key, e);
                    }
                });

                if (!window.ResizeObserver) {
                    window.ResizeObserver = ResizeObserverMock;
                }
                if (!g.ResizeObserver) {
                    g.ResizeObserver = window.ResizeObserver;
                }

                import("dom-storage").then(({default: Storage}) => {
                    const ensureStorage = (key) => {
                        let existing = null;
                        try {
                            existing = window[key];
                        } catch (e) {}

                        if (existing) {
                            g[key] = existing;
                            return;
                        }

                        const storage = new Storage(null, {strict: true});
                        const descriptor = Object.getOwnPropertyDescriptor(window, key);

                        if (!descriptor || descriptor.writable || typeof descriptor.set === "function") {
                            window[key] = storage;
                        } else if (descriptor.configurable === true) {
                            Object.defineProperty(window, key, {
                                value: storage,
                                configurable: true,
                            });
                        }

                        try {
                            g[key] = window[key] ?? storage;
                        } catch (e) {
                            g[key] = storage;
                        }
                    };

                    ensureStorage("localStorage");
                    ensureStorage("sessionStorage");

                    resolve(g);

                }).catch(e => {
                    console.error("Error loading dom-storage", e);
                    reject(e);
                });
            };

            const onLoad = () => {
                window.removeEventListener("load", onLoad);
                finalize();
            };

            window.addEventListener("load", onLoad);

            if (window.document.readyState === "complete") {
                queueMicrotask(finalize);
            } else {
                setTimeout(finalize, 50);
            }
        });
    });
}

export {initJSDOM, JSDOMExport} 
