import { globalCommitContext } from './commit-context.js';
// for better reuse if runtime is changed
export function factory({ createContext, useState, createElement, useLynxGlobalEventListener: useListener }, prop, eventName) {
    const Context = createContext({});
    const Provider = ({ children }) => {
        const [__, set] = useState(lynx[prop]);
        const handleChange = () => {
            if (prop === '__initData') {
                globalCommitContext.flushOptions.triggerDataUpdated = true;
            }
            set(lynx[prop]);
        };
        useChanged(handleChange);
        return createElement(Context.Provider, {
            value: __,
        }, children);
    };
    const Consumer = Context.Consumer;
    const use = () => {
        const [__, set] = useState(lynx[prop]);
        useChanged(() => {
            if (prop === '__initData') {
                globalCommitContext.flushOptions.triggerDataUpdated = true;
            }
            set(lynx[prop]);
        });
        return __;
    };
    const useChanged = (callback) => {
        if (!__LEPUS__) {
            useListener(eventName, callback);
        }
    };
    return {
        /* v8 ignore next */
        Context: () => Context,
        Provider: () => Provider,
        Consumer: () => Consumer,
        use: () => use,
        useChanged: () => useChanged,
    };
}
// dev-only: lets the page-data reset paths report an error when a reset meets this HOC.
let usesWithInitDataInState = false;
/**
 * @internal
 */
export function hasWithInitDataInStateUsage() {
    return usesWithInitDataInState;
}
/**
 * @internal
 */
export const RESET_WITH_INIT_DATA_IN_STATE_ERROR = 'Resetting page data does not clear the state injected by `withInitDataInState`: the HOC merges '
    + '`lynx.__initData` into the component state and cannot drop keys removed by the reset. '
    + 'Use `useInitData()` instead, or avoid combining a data reset with `withInitDataInState`.';
/**
 * Higher-Order Component (HOC) that injects `initData` into the state of the given class component.
 *
 * This HOC checks if the provided component is a class component. If it is, it wraps the component
 * and injects the `initData` into its state. It also adds a listener
 * to update the state when data changes, and removes the listener when the component unmounts.
 *
 * @typeParam P - The type of the props of the wrapped component.
 * @typeParam S - The type of the state of the wrapped component.
 *
 * @param App - The class component to be wrapped by the HOC.
 *
 * @returns The original component if it is not a class component, otherwise a new class component
 *          with `initData` injection and state update functionality.
 *
 * @example
 * ```typescript
 * class App extends React.Component<MyProps, MyState> {
 *   // component implementation
 * }
 *
 * export default withInitDataInState(App);
 * ```
 * @public
 */
export function withInitDataInState(App) {
    const isClassComponent = 'prototype' in App && 'render' in App.prototype;
    /* v8 ignore next 4 */
    if (!isClassComponent) {
        // return as-is when not class component
        return App;
    }
    class C extends App {
        h;
        constructor(props) {
            super(props);
            if (__DEV__) {
                usesWithInitDataInState = true;
            }
            this.state = {
                ...this.state,
                ...lynx.__initData,
            };
            if (!__LEPUS__) {
                lynx.getJSModule('GlobalEventEmitter').addListener('onDataChanged', this.h = (...args) => {
                    const [newData] = args;
                    globalCommitContext.flushOptions.triggerDataUpdated = true;
                    this.setState(newData);
                });
            }
        }
        componentWillUnmount() {
            super.componentWillUnmount?.();
            if (!__LEPUS__) {
                lynx.getJSModule('GlobalEventEmitter').removeListener('onDataChanged', this.h);
            }
        }
    }
    // Installed on the main thread only. There, `renderToString` reuses this component
    // instance across an `updatePage` re-render (the constructor never re-runs) and there
    // is no `onDataChanged` listener, so the constructor's one-time `initData` injection
    // goes stale; refresh it on every render, mirroring how `useInitData` re-reads
    // `lynx.__initData`. It is not installed on the background thread because defining
    // `getDerivedStateFromProps` at all would disable the wrapped component's legacy
    // `componentWillMount` / `componentWillReceiveProps` lifecycles — and the background
    // path already refreshes the state via its `onDataChanged` listener.
    if (__LEPUS__) {
        C.getDerivedStateFromProps = (props, state) => {
            const base = { ...state, ...lynx.__initData };
            // Compose with the wrapped component's own (or inherited) `getDerivedStateFromProps`
            // — passing it the freshened `initData` and letting its derived values win, matching
            // how it runs (and wins) on the background thread.
            const derived = App.getDerivedStateFromProps?.(props, base) ?? null;
            return { ...base, ...derived };
        };
    }
    return C;
}
//# sourceMappingURL=initData.js.map