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 _RealtimeEffectHandler_instances, _RealtimeEffectHandler_connect, _RealtimeEffectHandler_disconnect;
import { EffectType, RealtimeSubscriptionStatus, UIEventScope } from '@devvit/protos';
import { initDevvitGlobal } from '../../types/devvit-global.js';
import { EffectHandler } from './effect-handler.js';
initDevvitGlobal();
/**
 * This class encapsulates as much code as possible for possible future play
 * usage and to avoid shreddit iteration loops. It is subclassed in shreddit
 * by ShredditRealtimeHandler.
 */
export class RealtimeEffectHandler extends EffectHandler {
    constructor() {
        super(...arguments);
        _RealtimeEffectHandler_instances.add(this);
        // channel name is user data so use a naked object.
        this._channels = Object.create(null); // eslint-disable-line @typescript-eslint/naming-convention
        /** App called useChannel().send(). */
        // eslint-disable-next-line @typescript-eslint/naming-convention
        this._onSendEv = (ev) => {
            const { channel, data } = ev.detail.event;
            if (devvit?.debug.realtime)
                console.log(`[realtime] ${channel} sends`, data);
            // to-do: onEvent() so the app can throw an error.
            if (!(channel in this._channels))
                throw Error(`realtime ${channel} subscription required to send`);
            this.onSendSock(channel, data).catch((err) => {
                console.error(`[realtime] onSendSock() failed:`, err);
            });
        };
    }
    hostConnected() {
        super.hostConnected();
        if (devvit?.debug.realtime)
            console.log(`[realtime] host connected`);
        this.host.addEventListener('devvit-realtime-send', this._onSendEv);
        for (const channel in this._channels)
            if (this._channels[channel] === 'Disconnected')
                __classPrivateFieldGet(this, _RealtimeEffectHandler_instances, "m", _RealtimeEffectHandler_connect).call(this, channel, undefined);
    }
    hostDisconnected() {
        super.hostDisconnected();
        if (devvit?.debug.realtime)
            console.log(`[realtime] host disconnected`);
        for (const channel in this._channels)
            if (this._channels[channel] === 'Connected')
                __classPrivateFieldGet(this, _RealtimeEffectHandler_instances, "m", _RealtimeEffectHandler_disconnect).call(this, channel, undefined);
        this.host.removeEventListener('devvit-realtime-send', this._onSendEv);
    }
    onEvent(ev) {
        if (!ev.detail.effect.realtimeSubscriptions)
            return;
        if (ev.detail.effect.type !== EffectType.EFFECT_REALTIME_SUB)
            return;
        const channels = new Set(ev.detail.effect.realtimeSubscriptions.subscriptionIds);
        // App called useChannel().subscribe().
        for (const channel of channels)
            if (!this._channels[channel])
                __classPrivateFieldGet(this, _RealtimeEffectHandler_instances, "m", _RealtimeEffectHandler_connect).call(this, channel, ev.detail.onEvent);
        // App called useChannel().unsubscribe().
        for (const channel in this._channels)
            if (!channels.has(channel)) {
                if (this._channels[channel] === 'Connected')
                    __classPrivateFieldGet(this, _RealtimeEffectHandler_instances, "m", _RealtimeEffectHandler_disconnect).call(this, channel, ev.detail.onEvent);
                if (this._channels[channel] === 'Disconnected') {
                    this.onFreeSock(channel);
                    delete this._channels[channel];
                }
            }
    }
}
_RealtimeEffectHandler_instances = new WeakSet(), _RealtimeEffectHandler_connect = function _RealtimeEffectHandler_connect(channel, onEvent) {
    if (devvit?.debug.realtime)
        console.log(`[realtime] ${channel} connected`);
    // it might seem like this should be Disconnected but #connect() always
    // calls onConnectSock() and always sends subscribed. Disconnected is only
    // used to manage unwanted disconnects by the host.
    this._channels[channel] = 'Connected';
    onEvent?.({
        scope: UIEventScope.ALL,
        hook: realtimeChannelToHookID(channel),
        realtimeEvent: {
            event: { channel },
            status: RealtimeSubscriptionStatus.REALTIME_SUBSCRIBED,
        },
    });
    let callback = onEvent;
    if (devvit?.debug.realtime)
        callback = (ev) => {
            console.log(`[realtime] ${channel} receives`, ev.realtimeEvent?.event?.data);
            onEvent?.(ev);
        };
    this.onConnectSock(channel, callback);
}, _RealtimeEffectHandler_disconnect = function _RealtimeEffectHandler_disconnect(channel, onEvent) {
    if (devvit?.debug.realtime)
        console.log(`[realtime] ${channel} disconnected`);
    this.onDisconnectSock(channel);
    onEvent?.({
        scope: UIEventScope.ALL,
        hook: realtimeChannelToHookID(channel),
        realtimeEvent: {
            event: { channel },
            status: RealtimeSubscriptionStatus.REALTIME_UNSUBSCRIBED,
        },
    });
    this._channels[channel] = 'Disconnected';
};
export class DisabledRealtimeEffectHandler extends EffectHandler {
    constructor(host) {
        super(host);
        if (devvit?.debug.realtime)
            console.log(`[realtime] disabled`);
    }
    onEvent(ev) {
        if (!ev.detail.effect.realtimeSubscriptions)
            return;
        if (ev.detail.effect.type !== EffectType.EFFECT_REALTIME_SUB)
            return;
        throw Error('realtime permission required to un/subscribe');
    }
}
/**
 * Converts a UIEvent.RealtimeSubscriptionEvent.channel to UIEvent.hook. Eg:
 * `snntest20240521a:4feabe7a-fe98-47fa-97b5-ebc03e126918:locations` becomes
 * `useChannel:locations`. Maintain compatiblity with useChannel().
 */
export function realtimeChannelToHookID(channel) {
    return `useChannel:${channel.replace(/^.+:/, '')}`;
}
//# sourceMappingURL=realtime-effect-handler.js.map