// Playtest is a development mode for runtime-lite apps. When playtesting, the
// local runtime client may use a bidirectional socket to send and receive data
// such as sending local runtime logs to the Devvit CLI or receiving notice that
// a new version of the app has been installed and the client should be
// reloaded. The "playtest" term also applies to production apps that are being
// loaded in this development mode. Eg, observing a closed source app's logs
// with `devvit logs` even though we can't install new versions with
// `devvit playtest`.
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 __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 _PlaytestClient_instances, _PlaytestClient_state, _PlaytestClient_newOpening, _PlaytestClient_newRetrying, _PlaytestClient_onClose, _PlaytestClient_onMsg, _PlaytestClient_onOpen, _PlaytestClient_onRetry;
import { PlaytestSubscriberMessage, } from '@devvit/protos/types/devvit/cli/playtest.js';
import { playtestURI } from './PlaytestUtil.js';
/** Milliseconds to wait between reconnection attempts. */
const retryInterval = 15000;
/**
 * Playtest development mode socket state machine. Connects and maintains
 * connection to the Devvit CLI and calls back on significant state changes.
 *
 * ```
 *    ┌──────────────▲────────────────────▲
 *    │              │ close              │ close
 * ┌──▼─┐  open  ┌───┴───┐  onOpen    ┌───┴──┐
 * │Init├────────►Opening├────────────►Opened│
 * └────┘        └──┬─▲──┘  notify    └───┬──┘
 *           onClose│ │onRetry            │
 *               ┌──▼─┴───┐    onClose    │
 *               │Retrying◄───────────────┘
 *               └────────┘    notify
 * ```
 */
export class PlaytestClient {
    constructor() {
        _PlaytestClient_instances.add(this);
        _PlaytestClient_state.set(this, { type: 'Init' });
        _PlaytestClient_onClose.set(this, () => {
            if (__classPrivateFieldGet(this, _PlaytestClient_state, "f").type === 'Opened')
                this.onState?.('Closed');
            __classPrivateFieldSet(this, _PlaytestClient_state, __classPrivateFieldGet(this, _PlaytestClient_instances, "m", _PlaytestClient_newRetrying).call(this), "f");
        });
        _PlaytestClient_onMsg.set(this, (ev) => {
            const msg = PlaytestSubscriberMessage.fromJSON(JSON.parse(ev.data));
            // eslint-disable-next-line sonarjs/no-small-switch
            switch (true) {
                case msg.appInstalled != null:
                    this.onState?.('Reloading');
                    break;
            }
        });
        _PlaytestClient_onOpen.set(this, () => {
            if (__classPrivateFieldGet(this, _PlaytestClient_state, "f").type !== 'Opening')
                throw Error(`invalid transition from ${__classPrivateFieldGet(this, _PlaytestClient_state, "f").type}`);
            this.onState?.('Opened');
            __classPrivateFieldSet(this, _PlaytestClient_state, { sock: __classPrivateFieldGet(this, _PlaytestClient_state, "f").sock, type: 'Opened' }, "f");
            this.send({ connect: { url: location.href } });
        });
        _PlaytestClient_onRetry.set(this, () => {
            __classPrivateFieldSet(this, _PlaytestClient_state, __classPrivateFieldGet(this, _PlaytestClient_instances, "m", _PlaytestClient_newOpening).call(this), "f");
        });
    }
    /** The Close PlaytestState transition is not sent from this function. */
    close() {
        if ('timer' in __classPrivateFieldGet(this, _PlaytestClient_state, "f"))
            clearTimeout(__classPrivateFieldGet(this, _PlaytestClient_state, "f").timer);
        if ('sock' in __classPrivateFieldGet(this, _PlaytestClient_state, "f")) {
            __classPrivateFieldGet(this, _PlaytestClient_state, "f").sock.onopen = null;
            __classPrivateFieldGet(this, _PlaytestClient_state, "f").sock.onmessage = null;
            __classPrivateFieldGet(this, _PlaytestClient_state, "f").sock.onclose = null;
            __classPrivateFieldGet(this, _PlaytestClient_state, "f").sock.close();
        }
        __classPrivateFieldSet(this, _PlaytestClient_state, { type: 'Init' }, "f");
    }
    open() {
        if (__classPrivateFieldGet(this, _PlaytestClient_state, "f").type !== 'Init')
            return;
        __classPrivateFieldSet(this, _PlaytestClient_state, __classPrivateFieldGet(this, _PlaytestClient_instances, "m", _PlaytestClient_newOpening).call(this), "f");
    }
    /** Attempts to send data. If unopened or failure, data is lost. */
    send(msg) {
        if ('sock' in __classPrivateFieldGet(this, _PlaytestClient_state, "f"))
            __classPrivateFieldGet(this, _PlaytestClient_state, "f").sock.send(JSON.stringify(msg));
        else
            console.warn('devvit playtest message dropped', msg);
    }
}
_PlaytestClient_state = new WeakMap(), _PlaytestClient_onClose = new WeakMap(), _PlaytestClient_onMsg = new WeakMap(), _PlaytestClient_onOpen = new WeakMap(), _PlaytestClient_onRetry = new WeakMap(), _PlaytestClient_instances = new WeakSet(), _PlaytestClient_newOpening = function _PlaytestClient_newOpening() {
    // (There is no means to suppress DevTools console errors originating from a
    // failed connection.)
    const sock = new WebSocket(playtestURI);
    sock.onclose = __classPrivateFieldGet(this, _PlaytestClient_onClose, "f");
    sock.onmessage = __classPrivateFieldGet(this, _PlaytestClient_onMsg, "f");
    sock.onopen = __classPrivateFieldGet(this, _PlaytestClient_onOpen, "f");
    return { sock, type: 'Opening' };
}, _PlaytestClient_newRetrying = function _PlaytestClient_newRetrying() {
    return { timer: setTimeout(__classPrivateFieldGet(this, _PlaytestClient_onRetry, "f"), retryInterval), type: 'Retrying' };
};
