"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IrcClientRedisState = void 0;
const matrix_org_irc_1 = require("matrix-org-irc");
const types_1 = require("./types");
const Logger = __importStar(require("../logging"));
const log = Logger.get('IrcClientRedisState');
class IrcClientRedisState {
    redis;
    clientId;
    innerState;
    putStatePromise = Promise.resolve();
    static async create(redis, clientId, freshState) {
        log.debug(`Requesting ${freshState ? "fresh" : "existing"} state for ${clientId}`);
        const data = freshState ? null : await redis.hget(types_1.REDIS_IRC_CLIENT_STATE_KEY, clientId);
        const deseralisedData = data ? JSON.parse(data) : {};
        const chans = new Map();
        // In a previous iteration we failed to seralise this properly.
        deseralisedData.chans?.forEach(([channelName, chanData]) => {
            const isBuggyState = !Array.isArray(chanData.users);
            chans.set(channelName, {
                ...chanData,
                users: new Map(!isBuggyState ? chanData.users : []),
                modeParams: new Map(!isBuggyState ? chanData.modeParams : []),
            });
        });
        // We also had a bug where the supported state is bloated enormously
        if (deseralisedData.supportedState) {
            deseralisedData.supportedState.channel.modes = {
                a: [...new Set(deseralisedData.supportedState.channel.modes.a.split(''))].join(''),
                b: [...new Set(deseralisedData.supportedState.channel.modes.b.split(''))].join(''),
                c: [...new Set(deseralisedData.supportedState.channel.modes.c.split(''))].join(''),
                d: [...new Set(deseralisedData.supportedState.channel.modes.d.split(''))].join(''),
            };
            deseralisedData.supportedState.extra = [...new Set(deseralisedData.supportedState.extra)];
        }
        // The client library is currently responsible for flushing any new changes
        // to the state so we do not need to detect changes in this class.
        // In the future this may change.
        const innerState = {
            loggedIn: deseralisedData.loggedIn ?? false,
            registered: deseralisedData.registered ?? false,
            currentNick: deseralisedData.currentNick ?? '',
            nickMod: deseralisedData.nickMod ?? 0,
            whoisData: new Map(deseralisedData.whoisData),
            modeForPrefix: deseralisedData.modeForPrefix ?? {},
            hostMask: deseralisedData.hostMask ?? '',
            chans,
            maxLineLength: deseralisedData.maxLineLength ?? -1,
            lastSendTime: deseralisedData.lastSendTime ?? 0,
            prefixForMode: deseralisedData.prefixForMode ?? {},
            supportedState: deseralisedData.supportedState ?? {
                channel: {
                    idlength: {},
                    length: 200,
                    limit: {},
                    modes: { a: '', b: '', c: '', d: '' },
                    types: '',
                },
                kicklength: 0,
                maxlist: {},
                maxtargets: {},
                modes: 3,
                nicklength: 9,
                topiclength: 0,
                usermodes: '',
                usermodepriority: '',
                casemapping: 'ascii',
                extra: [],
            },
            capabilities: new matrix_org_irc_1.IrcCapabilities(deseralisedData.capabilities),
        };
        return new IrcClientRedisState(redis, clientId, innerState);
    }
    constructor(redis, clientId, innerState) {
        this.redis = redis;
        this.clientId = clientId;
        this.innerState = innerState;
    }
    get loggedIn() {
        return this.innerState.loggedIn;
    }
    set loggedIn(value) {
        this.innerState.loggedIn = value;
        this.flush();
    }
    get registered() {
        return this.innerState.registered;
    }
    set registered(value) {
        this.innerState.registered = value;
        this.flush();
    }
    get currentNick() {
        return this.innerState.currentNick;
    }
    set currentNick(value) {
        this.innerState.currentNick = value;
        this.flush();
    }
    get whoisData() {
        return this.innerState.whoisData;
    }
    set whoisData(value) {
        this.innerState.whoisData = value;
        this.flush();
    }
    get nickMod() {
        return this.innerState.nickMod;
    }
    set nickMod(value) {
        this.innerState.nickMod = value;
        this.flush();
    }
    get modeForPrefix() {
        return this.innerState.modeForPrefix;
    }
    set modeForPrefix(value) {
        this.innerState.modeForPrefix = value;
        this.flush();
    }
    get capabilities() {
        return this.innerState.capabilities;
    }
    set capabilities(value) {
        this.innerState.capabilities = value;
        this.flush();
    }
    get supportedState() {
        return this.innerState.supportedState;
    }
    set supportedState(value) {
        this.innerState.supportedState = value;
        this.flush();
    }
    get hostMask() {
        return this.innerState.hostMask;
    }
    set hostMask(value) {
        this.innerState.hostMask = value;
        this.flush();
    }
    get chans() {
        return this.innerState.chans;
    }
    set chans(value) {
        this.innerState.chans = value;
        this.flush();
    }
    get prefixForMode() {
        return this.innerState.prefixForMode;
    }
    set prefixForMode(value) {
        this.innerState.prefixForMode = value;
        this.flush();
    }
    get lastSendTime() {
        return this.innerState.lastSendTime;
    }
    set lastSendTime(value) {
        this.innerState.lastSendTime = value;
        this.flush();
    }
    flush() {
        const chans = [];
        this.innerState.chans.forEach((chanData, channelName) => {
            chans.push([
                channelName,
                {
                    ...chanData,
                    users: [...chanData.users.entries()],
                    modeParams: [...chanData.modeParams.entries()],
                }
            ]);
        });
        const serialState = JSON.stringify({
            ...this.innerState,
            whoisData: [...this.innerState.whoisData.entries()],
            chans,
            capabilities: this.innerState.capabilities.serialise(),
            supportedState: this.supportedState,
        });
        this.putStatePromise = this.putStatePromise.then(() => {
            return this.innerPutState(serialState).catch((ex) => {
                log.warn(`Failed to store state for ${this.clientId}`, ex);
            });
        });
    }
    async innerPutState(data) {
        await this.redis.hset(types_1.REDIS_IRC_CLIENT_STATE_KEY, this.clientId, data);
    }
}
exports.IrcClientRedisState = IrcClientRedisState;
//# sourceMappingURL=IrcClientRedisState.js.map