import { loadUnknownElementEventName, systemInfoBase, } from '../../constants.js';
import { BackgroundThread } from './Background.js';
import { I18nManager } from './I18n.js';
import { WASMJSBinding } from './elementAPIs/WASMJSBinding.js';
import { ExposureServices } from './ExposureServices.js';
import { createElementAPI } from './elementAPIs/createElementAPI.js';
import { createMainThreadGlobalAPIs } from './createMainThreadGlobalAPIs.js';
import { templateManager } from './TemplateManager.js';
import { loadAllWebElements } from '../webElementsDynamicLoader.js';
// @ts-expect-error
import IN_SHADOW_CSS_MODERN from '../../../css/in_shadow.css?inline';
import { requestIdleCallbackImpl } from './utils/requestIdleCallback.js';
loadAllWebElements().catch((e) => {
    console.error('[lynx-web] Failed to load web elements', e);
});
const IN_SHADOW_CSS = URL.createObjectURL(new Blob([IN_SHADOW_CSS_MODERN], { type: 'text/css' }));
const linkElement = document.createElement('link');
linkElement.rel = 'stylesheet';
linkElement.href = IN_SHADOW_CSS;
linkElement.type = 'text/css';
linkElement.fetchPriority = 'high';
linkElement.blocking = 'render';
const pixelRatio = window.devicePixelRatio;
const screenWidth = window.screen.availWidth * pixelRatio;
const screenHeight = window.screen.availHeight * pixelRatio;
export function createSystemInfo(browserConfig) {
    return Object.freeze({
        ...systemInfoBase,
        // some information only available on main thread, we should read and pass to worker
        pixelRatio,
        pixelWidth: screenWidth,
        pixelHeight: screenHeight,
        ...browserConfig,
    });
}
export class LynxViewInstance {
    parentDom;
    initData;
    globalprops;
    templateUrl;
    rootDom;
    mtsRealm;
    isSSR;
    transformVW;
    transformVH;
    transformREM;
    mainThreadGlobalThis;
    mtsWasmBinding;
    backgroundThread;
    i18nManager;
    exposureServices;
    webElementsLoadingPromises = [];
    #queryComponentCache = new Map();
    #pageConfig;
    #nativeModulesMap;
    #napiModulesMap;
    lepusCodeUrls = new Map();
    systemInfo;
    constructor(parentDom, initData, globalprops, templateUrl, rootDom, mtsRealm, isSSR, lynxGroupId, nativeModulesMap = {}, napiModulesMap = {}, initI18nResources, transformVW = false, transformVH = false, transformREM = false, browserConfig) {
        this.parentDom = parentDom;
        this.initData = initData;
        this.globalprops = globalprops;
        this.templateUrl = templateUrl;
        this.rootDom = rootDom;
        this.mtsRealm = mtsRealm;
        this.isSSR = isSSR;
        this.transformVW = transformVW;
        this.transformVH = transformVH;
        this.transformREM = transformREM;
        this.systemInfo = createSystemInfo(browserConfig);
        if (!isSSR) {
            this.rootDom.append(linkElement.cloneNode(false));
        }
        this.#nativeModulesMap = nativeModulesMap;
        this.#napiModulesMap = napiModulesMap;
        this.mainThreadGlobalThis = mtsRealm.globalWindow;
        this.backgroundThread = new BackgroundThread(lynxGroupId, this);
        this.i18nManager = new I18nManager(this.backgroundThread, this.rootDom, initI18nResources);
        this.mtsWasmBinding = new WASMJSBinding(this);
        this.exposureServices = new ExposureServices(this);
        this.backgroundThread.markTiming('create_lynx_start');
    }
    onPageConfigReady(config) {
        if (this.#pageConfig) {
            return;
        }
        // create element APIs
        this.#pageConfig = config;
        const enableCSSSelector = config['enableCSSSelector'] == 'true';
        const defaultDisplayLinear = config['defaultDisplayLinear'] == 'true';
        const defaultOverflowVisible = config['defaultOverflowVisible'] == 'true';
        Object.assign(this.mtsRealm.globalWindow, createElementAPI(this.rootDom, this.mtsWasmBinding, enableCSSSelector, defaultDisplayLinear, defaultOverflowVisible, this.transformVW, this.transformVH, this.transformREM), createMainThreadGlobalAPIs(this));
    }
    onStyleInfoReady(currentUrl) {
        if (this.mtsWasmBinding.wasmContext) {
            const resource = templateManager.getStyleSheet(currentUrl);
            if (resource) {
                this.mtsWasmBinding.wasmContext.push_style_sheet(resource, this.templateUrl === currentUrl ? undefined : currentUrl);
            }
        }
    }
    async onMTSScriptsLoaded(currentUrl, isLazy) {
        this.backgroundThread.markTiming('lepus_execute_start');
        const urlMap = templateManager.getBundle(currentUrl)
            ?.lepusCode;
        this.lepusCodeUrls.set(currentUrl, urlMap);
        if (!isLazy) {
            await this.mtsRealm.loadScript(urlMap['root']);
            this.onMTSScriptsExecuted();
        }
    }
    onMTSScriptsExecuted() {
        this.backgroundThread.markTiming('lepus_execute_end');
        this.webElementsLoadingPromises.length = 0;
        this.backgroundThread.markTiming('data_processor_start');
        const processedData = this.#pageConfig?.['enableJSDataProcessor'] !== 'true'
            && this.mainThreadGlobalThis.processData
            ? this.mainThreadGlobalThis.processData?.(this.initData)
            : this.initData;
        this.backgroundThread.markTiming('data_processor_end');
        this.backgroundThread.startWebWorker(processedData, this.globalprops, templateManager.getBundle(this.templateUrl).config.cardType, templateManager.getBundle(this.templateUrl)?.customSections, this.#nativeModulesMap, this.#napiModulesMap);
        if (this.isSSR) {
            this.rootDom.querySelector('[part="page"]')?.remove();
        }
        this.mainThreadGlobalThis.renderPage?.(processedData);
        this.mainThreadGlobalThis.__FlushElementTree();
    }
    async onBTSScriptsLoaded(url) {
        const btsUrls = templateManager.getBundle(url)
            ?.backgroundCode;
        await this.backgroundThread.updateBTSChunk(url, btsUrls);
        this.backgroundThread.startBTS();
    }
    loadUnknownElement(tagName) {
        if (tagName.includes('-') && !customElements.get(tagName)) {
            this.rootDom.dispatchEvent(new CustomEvent(loadUnknownElementEventName, {
                detail: {
                    tagName,
                },
            }));
            this.webElementsLoadingPromises.push(customElements.whenDefined(tagName).then(() => { }));
        }
    }
    queryComponent(url) {
        if (this.#queryComponentCache.has(url)) {
            return this.#queryComponentCache.get(url);
        }
        const promise = templateManager.fetchBundle(url, Promise.resolve(this), this.transformVW, this.transformVH, this.transformREM, {
            enableCSSSelector: this.#pageConfig['enableCSSSelector'],
        })
            .then(async () => {
            const urlMap = this.lepusCodeUrls.get(url);
            const rootUrl = urlMap?.['root'];
            if (!rootUrl) {
                throw new Error(`[lynx-web] Missing root URL for component: ${url}`);
            }
            let lepusRootChunkExport = await this.mtsRealm.loadScript(rootUrl);
            lepusRootChunkExport = this.mainThreadGlobalThis.processEvalResult?.(lepusRootChunkExport, url) ?? lepusRootChunkExport;
            return lepusRootChunkExport;
        });
        this.#queryComponentCache.set(url, promise);
        return promise;
    }
    async updateData(data, processorName) {
        const processedData = this.#pageConfig['enableJSDataProcessor'] !== 'true'
            && this.mainThreadGlobalThis.processData
            ? this.mainThreadGlobalThis.processData(data, processorName)
            : data;
        this.mainThreadGlobalThis.updatePage?.(processedData, { processorName });
        await this.backgroundThread.updateData(processedData, { processorName });
    }
    async updateGlobalProps(data) {
        await this.backgroundThread.updateGlobalProps(data);
    }
    reportError(error, release, fileName) {
        this.rootDom.dispatchEvent(new CustomEvent('error', {
            detail: {
                sourceMap: {
                    offset: {
                        line: 2,
                        col: 0,
                    },
                },
                error,
                release,
                fileName,
            },
            bubbles: true,
            cancelable: true,
            composed: true,
        }));
    }
    async [Symbol.asyncDispose]() {
        await this.backgroundThread[Symbol.asyncDispose]();
        this.exposureServices.dispose();
        requestIdleCallbackImpl(() => {
            this.mtsWasmBinding.dispose();
        });
    }
}
//# sourceMappingURL=LynxViewInstance.js.map