// Copyright 2024 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { markTiming, setPipeline } from './performance.js';
import { __root, setRoot } from '../../root.js';
import { isEmptyObject } from '../../utils.js';
import { LifecycleConstant } from '../lifecycle/constant.js';
import { isJSReady, jsReady, jsReadyEventIdSwap, resetJSReady } from '../lifecycle/event/jsReady.js';
import { reloadMainThread } from '../lifecycle/reload.js';
import { renderMainThread } from '../lifecycle/render.js';
import { __pendingListUpdates } from '../list/pendingListUpdates.js';
import { hydrate } from '../renderToOpcodes/hydrate.js';
import { ssrHydrateByOpcodes } from '../renderToOpcodes/opcodes.js';
import { __page, setupPage } from '../snapshot/definition.js';
import { SnapshotInstance } from '../snapshot/snapshot.js';
import { applyRefQueue } from '../snapshot/workletRef.js';
function ssrEncode() {
    const { __opcodes } = __root;
    delete __root.__opcodes;
    // eslint-disable-next-line @typescript-eslint/unbound-method
    const oldToJSON = SnapshotInstance.prototype.toJSON;
    SnapshotInstance.prototype.toJSON = function () {
        return [
            this.type,
            this.__id,
            this.__elements,
        ];
    };
    try {
        const replacer = (_key, value) => {
            if (value && typeof value === 'object' && '_wkltId' in value) {
                return null;
            }
            return value;
        };
        return JSON.stringify({ __opcodes, __root_values: __root.__values }, replacer);
    }
    finally {
        SnapshotInstance.prototype.toJSON = oldToJSON;
    }
}
function ssrHydrate(info) {
    const nativePage = __GetPageElement();
    if (!nativePage) {
        throw new Error('SSR Hydration Failed! Please check if the SSR content loaded successfully!');
    }
    resetJSReady();
    setupPage(nativePage);
    const refsMap = __GetTemplateParts(nativePage);
    const { __opcodes, __root_values } = JSON.parse(info);
    if (__root_values) {
        __root.setAttribute('values', __root_values);
    }
    ssrHydrateByOpcodes(__opcodes, __root, refsMap);
    __root.__elements = [nativePage];
    __root.__element_root = nativePage;
}
function injectCalledByNative() {
    if (process.env['NODE_ENV'] !== 'test' && __FIRST_SCREEN_SYNC_TIMING__ !== 'jsReady' && __ENABLE_SSR__) {
        throw new Error('`firstScreenSyncTiming` must be `jsReady` when SSR is enabled');
    }
    const calledByNative = {
        renderPage,
        updatePage,
        updateGlobalProps,
        getPageData: function () {
            return null;
        },
        removeComponents: function () { },
        ...(__ENABLE_SSR__ ? { ssrEncode, ssrHydrate } : {}),
    };
    Object.assign(globalThis, calledByNative);
    Object.assign(globalThis, {
        [LifecycleConstant.jsReady]: jsReady,
    });
}
function renderPage(data) {
    // reset `jsReady` state
    resetJSReady();
    lynx.__initData = data ?? {};
    setupPage(__CreatePage('0', 0));
    __root.ensureElements();
    renderMainThread();
    // always call this before `__FlushElementTree`
    // (There is an implicit `__FlushElementTree` in `renderPage`)
    __pendingListUpdates.flush();
    applyRefQueue();
    if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') {
        jsReady();
    }
}
function updatePage(data, options) {
    if (options?.reloadTemplate) {
        reloadMainThread(data, options);
        return;
    }
    if (options?.resetPageData) {
        lynx.__initData = {};
    }
    if (typeof data == 'object' && !isEmptyObject(data)) {
        lynx.__initData ??= {};
        Object.assign(lynx.__initData, data);
    }
    const flushOptions = options ?? {};
    if (!isJSReady) {
        const oldRoot = __root;
        setRoot(new SnapshotInstance('root'));
        __root.__jsx = oldRoot.__jsx;
        setPipeline(options?.pipelineOptions);
        markTiming('updateDiffVdomStart');
        {
            __pendingListUpdates.clearAttachedLists();
            renderMainThread();
            // As said by codename `jsReadyEventIdSwap`, this swap will only be used for event remap,
            // because ref & unref cause by previous render will be ignored
            hydrate(oldRoot, __root, { skipUnRef: true, swap: jsReadyEventIdSwap });
            // always call this before `__FlushElementTree`
            __pendingListUpdates.flush();
            applyRefQueue();
        }
        flushOptions.triggerDataUpdated = true;
        markTiming('updateDiffVdomEnd');
    }
    __FlushElementTree(__page, flushOptions);
}
function updateGlobalProps(_data, options) {
    if (options) {
        __FlushElementTree(__page, options);
    }
    else {
        __FlushElementTree();
    }
}
/**
 * @internal
 */
export { injectCalledByNative };
//# sourceMappingURL=calledByNative.js.map