var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _UnsandboxedRuntimeLite_instances, _UnsandboxedRuntimeLite_actor, _UnsandboxedRuntimeLite_aborted, _UnsandboxedRuntimeLite_methodMsgMap, _UnsandboxedRuntimeLite_plugins, _UnsandboxedRuntimeLite_pluginCallId, _UnsandboxedRuntimeLite_pluginReturnHandlers, _UnsandboxedRuntimeLite_localMetadata, _UnsandboxedRuntimeLite_dependencyMetadata, _UnsandboxedRuntimeLite_sourceMap, _UnsandboxedRuntimeLite_exceptionHandler, _UnsandboxedRuntimeLite_config, _UnsandboxedRuntimeLite_logErr, _UnsandboxedRuntimeLite_setupGlobalDevvit, _UnsandboxedRuntimeLite_runWithExceptionHandlerSync, _UnsandboxedRuntimeLite_runWithExceptionHandler;
import * as protos from '@devvit/protos';
import { CircuitBreak, isCircuitBreaker } from '@devvit/shared-types/CircuitBreaker.js';
import { QueueConsole } from '../console/QueueConsole.js';
import { ActorResponse } from '../types/Commands.js';
import { inspect } from '../utils/inspect.js';
import { getDependencyMetadata } from '../utils/metadata.js';
import { box } from './RuntimeLite.js';
export class UnsandboxedRuntimeLite {
    /**
     * @arg modules Environment dependencies available via `require(id)` to
     *              bundles loaded. @devvit/protos is provided by default.
     */
    constructor(modules = {}, exceptionHandler) {
        _UnsandboxedRuntimeLite_instances.add(this);
        _UnsandboxedRuntimeLite_actor.set(this, void 0);
        _UnsandboxedRuntimeLite_aborted.set(this, false);
        _UnsandboxedRuntimeLite_methodMsgMap.set(this, {});
        _UnsandboxedRuntimeLite_plugins.set(this, []);
        _UnsandboxedRuntimeLite_pluginCallId.set(this, 0);
        _UnsandboxedRuntimeLite_pluginReturnHandlers.set(this, {});
        _UnsandboxedRuntimeLite_localMetadata.set(this, {});
        _UnsandboxedRuntimeLite_dependencyMetadata.set(this, {});
        _UnsandboxedRuntimeLite_sourceMap.set(this, '');
        _UnsandboxedRuntimeLite_exceptionHandler.set(this, void 0);
        /**
         * Create a Config object used to construct a new Actor with all dependencies stubbed out
         */
        _UnsandboxedRuntimeLite_config.set(this, (assets, webviewAssets) => ({
            assets,
            webviewAssets,
            use: (def) => {
                if (__classPrivateFieldGet(this, _UnsandboxedRuntimeLite_plugins, "f").includes(def.fullName)) {
                    const proxy = {};
                    for (const method of Object.values(def.methods)) {
                        proxy[method.name] = async (req, metadata) => {
                            var _a;
                            const id = __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_pluginCallId, (_a = __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_pluginCallId, "f"), ++_a), "f");
                            const handler = new Promise((resolve, reject) => {
                                __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_pluginReturnHandlers, "f")[id] = { resolve, reject };
                            });
                            box.postMessage([
                                ActorResponse.PLUGIN_CALL,
                                id,
                                def.fullName,
                                method.name,
                                req,
                                metadata,
                            ]);
                            return handler;
                        };
                    }
                    return proxy;
                }
                else {
                    const circuitBreaker = {};
                    for (const method of Object.values(def.methods)) {
                        circuitBreaker[method.name] = () => {
                            __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_aborted, true, "f");
                            box.console.logs = [];
                            throw CircuitBreak(method.name);
                        };
                    }
                    return circuitBreaker;
                }
            },
            provides: (definition) => {
                // capture the (de)serializers for messages as they're added to the provide map
                Object.values(definition.methods).forEach((method) => {
                    __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_methodMsgMap, "f")[method.name] = {
                        req: method.requestType,
                        res: method.responseType,
                    };
                });
            },
        }));
        modules = { '@devvit/protos': protos, ...modules };
        __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_exceptionHandler, exceptionHandler, "f");
        box.require = (module) => modules[module];
        box.fetch = () => {
            box.log('[unsandboxed runtime] circuit break: fetch');
            __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_aborted, true, "f");
            box.console.logs = [];
            throw CircuitBreak('fetch');
        };
        box.clearCircuitBreak = () => {
            __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_aborted, false, "f");
            box.log('[unsandboxed runtime] circuit break cleared');
        };
    }
    enableAppLogging() {
        box.console = new QueueConsole();
    }
    getCallState() {
        return { logs: box.console.logs ?? [] };
    }
    async init() {
        box.log('[unsandboxed runtime] init');
    }
    setPluginList(plugins) {
        __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_plugins, plugins, "f");
    }
    setLocalMetadata(meta) {
        __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_localMetadata, meta, "f");
    }
    loadBundle(bundle) {
        box.console.logs = [];
        __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_dependencyMetadata, getDependencyMetadata(bundle), "f");
        const module = {};
        if (bundle.sourceMap) {
            __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_sourceMap, bundle.sourceMap, "f");
        }
        __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_runWithExceptionHandlerSync).call(this, () => {
            // eslint-disable-next-line security/detect-eval-with-expression
            eval(bundle.code);
        });
        const config = __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_config, "f").call(this, bundle.assets, bundle.webviewAssets);
        __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_setupGlobalDevvit).call(this, config);
        __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_runWithExceptionHandlerSync).call(this, () => {
            if (!module?.exports?.default)
                throw Error('bundle missing default export');
            __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_actor, new module.exports.default(config), "f");
        });
    }
    async callBinary(method, args, meta) {
        return __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_runWithExceptionHandler).call(this, async () => {
            const { req, res } = __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_methodMsgMap, "f")[method];
            const argsObj = req.decode(args);
            const response = await this.call(method, argsObj, meta, false);
            if (response.value) {
                response.value = res.encode(res.fromJSON(response.value)).finish();
            }
            return response;
        });
    }
    async call(method, args, meta, binary) {
        if (binary) {
            // temporary solution until the binary flag can be dropped
            return await this.callBinary(method, args, meta);
        }
        __classPrivateFieldSet(this, _UnsandboxedRuntimeLite_aborted, false, "f");
        box.console.logs = [];
        try {
            // The order of metadata is intentional here:
            // - Use the local environment metadata first
            // - Allow per-call overrides with call metadata
            // - But do not allow `devvit-dependencies` to be overwritten
            const callMetadata = { ...__classPrivateFieldGet(this, _UnsandboxedRuntimeLite_localMetadata, "f"), ...meta, ...__classPrivateFieldGet(this, _UnsandboxedRuntimeLite_dependencyMetadata, "f") };
            const value = await __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_runWithExceptionHandler).call(this, () => {
                return __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_actor, "f")?.[method]?.(args, callMetadata);
            });
            if (__classPrivateFieldGet(this, _UnsandboxedRuntimeLite_aborted, "f")) {
                box.console.logs = [];
                throw CircuitBreak(method);
            }
            return { error: undefined, success: true, value };
        }
        catch (err) {
            __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_logErr).call(this, err);
            // Do not use the response. The circuit break was uncleared which may
            // indicate that the break Error was caught unintentionally.
            return { error: err, success: false, value: undefined };
        }
    }
    onPluginResponse(id, result) {
        const handler = __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_pluginReturnHandlers, "f")[id];
        if (result.success) {
            handler.resolve(result.value);
        }
        else {
            handler.reject(result.error);
        }
        delete __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_pluginReturnHandlers, "f")[id];
    }
    quit() {
        box.log('[unsandboxed runtime] quitting');
        box.close();
    }
}
_UnsandboxedRuntimeLite_actor = new WeakMap(), _UnsandboxedRuntimeLite_aborted = new WeakMap(), _UnsandboxedRuntimeLite_methodMsgMap = new WeakMap(), _UnsandboxedRuntimeLite_plugins = new WeakMap(), _UnsandboxedRuntimeLite_pluginCallId = new WeakMap(), _UnsandboxedRuntimeLite_pluginReturnHandlers = new WeakMap(), _UnsandboxedRuntimeLite_localMetadata = new WeakMap(), _UnsandboxedRuntimeLite_dependencyMetadata = new WeakMap(), _UnsandboxedRuntimeLite_sourceMap = new WeakMap(), _UnsandboxedRuntimeLite_exceptionHandler = new WeakMap(), _UnsandboxedRuntimeLite_config = new WeakMap(), _UnsandboxedRuntimeLite_instances = new WeakSet(), _UnsandboxedRuntimeLite_logErr = function _UnsandboxedRuntimeLite_logErr(err) {
    var _a;
    // Don't log circuit breaks. The runtime operates optimistically. When
    // it fails, all side-effects are rolled back. Logs should not be reported.
    if (!isCircuitBreaker(err)) {
        (_a = box.console).logs ?? (_a.logs = []);
        box.console.logs.push({ method: 'error', args: [inspect(err)] });
    }
}, _UnsandboxedRuntimeLite_setupGlobalDevvit = function _UnsandboxedRuntimeLite_setupGlobalDevvit(config) {
    var _a;
    const globalScope = globalThis;
    globalScope.devvit ?? (globalScope.devvit = {});
    (_a = globalScope.devvit).config ?? (_a.config = {
        assets: config.assets,
    });
}, _UnsandboxedRuntimeLite_runWithExceptionHandlerSync = function _UnsandboxedRuntimeLite_runWithExceptionHandlerSync(executionFn) {
    try {
        return executionFn();
    }
    catch (err) {
        const error = err instanceof Error ? err : undefined;
        __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_logErr).call(this, err);
        if (error?.stack && __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_sourceMap, "f")) {
            __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_exceptionHandler, "f")?.handleException(__classPrivateFieldGet(this, _UnsandboxedRuntimeLite_sourceMap, "f"), error);
        }
        throw err;
    }
}, _UnsandboxedRuntimeLite_runWithExceptionHandler = async function _UnsandboxedRuntimeLite_runWithExceptionHandler(executionFn) {
    try {
        return await executionFn();
    }
    catch (err) {
        const error = err instanceof Error ? err : undefined;
        __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_instances, "m", _UnsandboxedRuntimeLite_logErr).call(this, err);
        if (error?.stack && __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_sourceMap, "f")) {
            __classPrivateFieldGet(this, _UnsandboxedRuntimeLite_exceptionHandler, "f")?.handleException(__classPrivateFieldGet(this, _UnsandboxedRuntimeLite_sourceMap, "f"), error);
        }
        throw err;
    }
};
