/// <reference lib="dom"/>
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 _BrowserLiteClient_instances, _BrowserLiteClient_workerCode, _BrowserLiteClient_worker, _BrowserLiteClient_ready, _BrowserLiteClient_callId, _BrowserLiteClient_bundle, _BrowserLiteClient_returnHandlers, _BrowserLiteClient_onLoadHandler, _BrowserLiteClient_onError, _BrowserLiteClient_pluginOptions, _BrowserLiteClient_playtest, _BrowserLiteClient_playtestFilter, _BrowserLiteClient_runtimeDebugLogging, _BrowserLiteClient_runtimeInitMetric, _BrowserLiteClient_handleResponse, _BrowserLiteClient_loadPlugins, _BrowserLiteClient_log, _BrowserLiteClient_callLoadBundle, _BrowserLiteClient_onPlaytest;
import { Severity } from '@devvit/protos';
import { CircuitBreakerResponse } from '@devvit/shared-types/CircuitBreaker.js';
import { ActorCommand, ActorResponse } from '../types/Commands.js';
import { getAppName, getAppTitle } from './ClientShared.js';
import { PlaytestClient } from './playtest/PlaytestClient.js';
import { getPlaytestFilter, isPlaytest, playtestQueryParam } from './playtest/PlaytestUtil.js';
const severityByAppMethod = {
    debug: Severity.DEBUG,
    error: Severity.ERROR,
    info: Severity.INFO,
    log: Severity.DEBUG,
    trace: Severity.DEBUG,
    warn: Severity.WARN,
};
/**
 * Wraps worker, runtime lifecycles, and call un/packing.
 *
 * Used by Shreddit via devvit-custom-post. A feed would have one client per
 * post. Not used by native mobile apps which doesn't have workers.
 *
 * Web workers can run code from data URI or URL of window origin.
 */
export class BrowserLiteClient {
    /** @arg onError Worker error event handler. */
    constructor(workerCode, onError, pluginOptions) {
        _BrowserLiteClient_instances.add(this);
        /** The runtime. */
        _BrowserLiteClient_workerCode.set(this, void 0);
        _BrowserLiteClient_worker.set(this, void 0);
        _BrowserLiteClient_ready.set(this, false);
        _BrowserLiteClient_callId.set(this, 0);
        _BrowserLiteClient_bundle.set(this, void 0);
        _BrowserLiteClient_returnHandlers.set(this, {});
        _BrowserLiteClient_onLoadHandler.set(this, void 0);
        _BrowserLiteClient_onError.set(this, () => { });
        _BrowserLiteClient_pluginOptions.set(this, void 0);
        _BrowserLiteClient_playtest.set(this, void 0);
        /** Save filter URL state at creation time. */
        _BrowserLiteClient_playtestFilter.set(this, getPlaytestFilter());
        _BrowserLiteClient_runtimeDebugLogging.set(this, false);
        _BrowserLiteClient_runtimeInitMetric.set(this, void 0);
        _BrowserLiteClient_handleResponse.set(this, (ev) => {
            const rsp = ev.data;
            const [cmd] = rsp;
            switch (cmd) {
                case ActorResponse.READY: {
                    const [, init] = rsp;
                    __classPrivateFieldSet(this, _BrowserLiteClient_runtimeInitMetric, init, "f");
                    if (__classPrivateFieldGet(this, _BrowserLiteClient_pluginOptions, "f")?.plugins) {
                        __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_loadPlugins).call(this);
                    }
                    else {
                        __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_callLoadBundle).call(this);
                    }
                    break;
                }
                case ActorResponse.PLUGINS_CONFIGURED:
                    __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_callLoadBundle).call(this);
                    break;
                case ActorResponse.BUNDLE_LOADED: {
                    const [, logs] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_log).call(this, logs);
                    __classPrivateFieldSet(this, _BrowserLiteClient_ready, true, "f");
                    __classPrivateFieldGet(this, _BrowserLiteClient_onLoadHandler, "f")?.resolve({ metrics: { init: __classPrivateFieldGet(this, _BrowserLiteClient_runtimeInitMetric, "f") } });
                    __classPrivateFieldSet(this, _BrowserLiteClient_onLoadHandler, undefined, "f");
                    break;
                }
                case ActorResponse.BUNDLE_LOAD_FAILED: {
                    const [, , err, logs] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_log).call(this, logs);
                    __classPrivateFieldGet(this, _BrowserLiteClient_onLoadHandler, "f")?.reject(err);
                    __classPrivateFieldSet(this, _BrowserLiteClient_onLoadHandler, undefined, "f");
                    break;
                }
                case ActorResponse.RETURN: {
                    const [, id, response, logs] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_log).call(this, logs);
                    __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id]?.resolve(response);
                    delete __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id];
                    break;
                }
                case ActorResponse.ERROR: {
                    const [, id, err, logs] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_log).call(this, logs);
                    __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id]?.reject(err);
                    delete __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id];
                    break;
                }
                case ActorResponse.CIRCUIT_BREAKER: {
                    const [, id, response, cause] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id]?.reject(new CircuitBreakerResponse(response, cause));
                    delete __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id];
                    break;
                }
                case ActorResponse.PLUGIN_CALL: {
                    const [, id, serviceName, method, args, meta] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_pluginOptions, "f")?.callback(id, serviceName, method, args, meta);
                    break;
                }
                case ActorResponse.UNHANDLED_REJECTION: {
                    const [, , reason, logs] = rsp;
                    __classPrivateFieldGet(this, _BrowserLiteClient_instances, "m", _BrowserLiteClient_log).call(this, logs);
                    __classPrivateFieldGet(this, _BrowserLiteClient_onError, "f").call(this, 'UnhandledRejection', reason);
                    break;
                }
            }
        });
        _BrowserLiteClient_onPlaytest.set(this, (state) => {
            // This state can only be received while in playtest mode.
            switch (state) {
                case 'Opened':
                    console.info('devvit playtest connected'); // to-do: emit and toast.
                    break;
                case 'Closed':
                    console.info('devvit playtest disconnected'); // to-do: emit and toast.
                    break;
                case 'Reloading': {
                    // to-do: emit and toast. Let the receiver decide if they wish to reload
                    // and to manage the location.
                    console.info('devvit playtest reloading');
                    __classPrivateFieldGet(this, _BrowserLiteClient_playtest, "f")?.close();
                    // The user may have since changed URLs but expects playtest to continue
                    // with the same filter.
                    const url = new URL(location.href);
                    url.searchParams.set(playtestQueryParam, __classPrivateFieldGet(this, _BrowserLiteClient_playtestFilter, "f") ?? '');
                    location.assign(url);
                    // Reload the page, don't just navigate.
                    location.reload();
                    break;
                }
            }
        });
        __classPrivateFieldSet(this, _BrowserLiteClient_workerCode, workerCode, "f");
        if (onError)
            __classPrivateFieldSet(this, _BrowserLiteClient_onError, onError, "f");
        if (pluginOptions)
            __classPrivateFieldSet(this, _BrowserLiteClient_pluginOptions, pluginOptions, "f");
    }
    get ready() {
        return __classPrivateFieldGet(this, _BrowserLiteClient_ready, "f");
    }
    get runtimeDebugLogging() {
        return __classPrivateFieldGet(this, _BrowserLiteClient_runtimeDebugLogging, "f");
    }
    set runtimeDebugLogging(on) {
        if (on === __classPrivateFieldGet(this, _BrowserLiteClient_runtimeDebugLogging, "f"))
            return;
        __classPrivateFieldSet(this, _BrowserLiteClient_runtimeDebugLogging, on, "f");
        if (this.runtimeDebugLogging)
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")?.postMessage([ActorCommand.ENABLE_DEBUG_LOGGING]);
    }
    /**
     * This function doesn't actually load a bundle by itself. It creates a new
     * worker, evaluates the entrypoint, and waits for ActorResponse.READY which
     * eventually kicks off a bundle load.
     */
    async loadBundle(bundle) {
        if (__classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")) {
            throw new Error('Bundle already loaded');
        }
        const playtest = isPlaytest(getAppName(bundle));
        if (playtest) {
            __classPrivateFieldSet(this, _BrowserLiteClient_playtest, new PlaytestClient(), "f");
            __classPrivateFieldGet(this, _BrowserLiteClient_playtest, "f").onState = __classPrivateFieldGet(this, _BrowserLiteClient_onPlaytest, "f");
        }
        __classPrivateFieldGet(this, _BrowserLiteClient_playtest, "f")?.open();
        const worker = new Worker(URL.createObjectURL(__classPrivateFieldGet(this, _BrowserLiteClient_workerCode, "f")), {
            name: `App: ${getAppTitle(bundle)}`,
            type: 'module',
        });
        worker.onerror = (ev) => __classPrivateFieldGet(this, _BrowserLiteClient_onError, "f").call(this, 'Error', ev.error);
        worker.onmessageerror = (ev) => __classPrivateFieldGet(this, _BrowserLiteClient_onError, "f").call(this, 'MessageError', ev.data);
        worker.onmessage = __classPrivateFieldGet(this, _BrowserLiteClient_handleResponse, "f");
        __classPrivateFieldSet(this, _BrowserLiteClient_worker, worker, "f");
        __classPrivateFieldSet(this, _BrowserLiteClient_bundle, bundle, "f");
        if (this.runtimeDebugLogging)
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").postMessage([ActorCommand.ENABLE_DEBUG_LOGGING]);
        if (playtest)
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").postMessage([ActorCommand.ENABLE_APP_LOGGING]);
        return new Promise((resolve, reject) => {
            __classPrivateFieldSet(this, _BrowserLiteClient_onLoadHandler, { resolve, reject }, "f");
        });
    }
    async call(method, args, meta) {
        var _a;
        if (!__classPrivateFieldGet(this, _BrowserLiteClient_ready, "f") || !__classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")) {
            throw new Error('No bundle loaded');
        }
        const id = __classPrivateFieldSet(this, _BrowserLiteClient_callId, (_a = __classPrivateFieldGet(this, _BrowserLiteClient_callId, "f"), ++_a), "f");
        const handler = new Promise((resolve, reject) => {
            __classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")[id] = { resolve, reject };
        });
        __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").postMessage([ActorCommand.CALL, id, method, args, meta, false]);
        return handler;
    }
    onPluginResponse(id, result) {
        __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")?.postMessage([ActorCommand.PLUGIN_RESPONSE, id, result]);
    }
    /** Call this method before repeating a call to loadBundle(). */
    quit() {
        __classPrivateFieldGet(this, _BrowserLiteClient_playtest, "f")?.close();
        __classPrivateFieldSet(this, _BrowserLiteClient_playtest, undefined, "f");
        __classPrivateFieldSet(this, _BrowserLiteClient_ready, false, "f");
        if (__classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")) {
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").postMessage([ActorCommand.QUIT]);
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").onerror = null;
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").onmessageerror = null;
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").onmessage = null;
            __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f").terminate();
        }
        __classPrivateFieldSet(this, _BrowserLiteClient_worker, undefined, "f");
        for (const { reject } of Object.values(__classPrivateFieldGet(this, _BrowserLiteClient_returnHandlers, "f")))
            reject(Error('worker terminated'));
        __classPrivateFieldSet(this, _BrowserLiteClient_returnHandlers, {}, "f");
        __classPrivateFieldGet(this, _BrowserLiteClient_onLoadHandler, "f")?.reject(Error('worker terminated'));
        __classPrivateFieldSet(this, _BrowserLiteClient_onLoadHandler, undefined, "f");
    }
}
_BrowserLiteClient_workerCode = new WeakMap(), _BrowserLiteClient_worker = new WeakMap(), _BrowserLiteClient_ready = new WeakMap(), _BrowserLiteClient_callId = new WeakMap(), _BrowserLiteClient_bundle = new WeakMap(), _BrowserLiteClient_returnHandlers = new WeakMap(), _BrowserLiteClient_onLoadHandler = new WeakMap(), _BrowserLiteClient_onError = new WeakMap(), _BrowserLiteClient_pluginOptions = new WeakMap(), _BrowserLiteClient_playtest = new WeakMap(), _BrowserLiteClient_playtestFilter = new WeakMap(), _BrowserLiteClient_runtimeDebugLogging = new WeakMap(), _BrowserLiteClient_runtimeInitMetric = new WeakMap(), _BrowserLiteClient_handleResponse = new WeakMap(), _BrowserLiteClient_onPlaytest = new WeakMap(), _BrowserLiteClient_instances = new WeakSet(), _BrowserLiteClient_loadPlugins = function _BrowserLiteClient_loadPlugins() {
    __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")?.postMessage([ActorCommand.ENABLE_PLUGINS, __classPrivateFieldGet(this, _BrowserLiteClient_pluginOptions, "f")?.plugins ?? []]);
}, _BrowserLiteClient_log = function _BrowserLiteClient_log(logs) {
    if (!logs.length)
        return;
    // This isn't really correct but the runtime doesn't care and waiting until
    // it gets to the client is even worse.
    const now = new Date();
    const callState = {
        logs: logs.map((log) => ({
            severity: severityByAppMethod[log.method],
            message: log.args.join(' '),
            tags: [],
            timestamp: now,
        })),
    };
    __classPrivateFieldGet(this, _BrowserLiteClient_playtest, "f")?.send({ callState });
}, _BrowserLiteClient_callLoadBundle = function _BrowserLiteClient_callLoadBundle() {
    __classPrivateFieldGet(this, _BrowserLiteClient_worker, "f")?.postMessage([ActorCommand.LOAD_BUNDLE, __classPrivateFieldGet(this, _BrowserLiteClient_bundle, "f")]);
    __classPrivateFieldSet(this, _BrowserLiteClient_bundle, undefined, "f");
};
