"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Socket = exports.SocketService = void 0;
const mongodb_1 = require("mongodb");
const socket_io_1 = __importDefault(require("socket.io"));
const Loggify_1 = require("../decorators/Loggify");
const logger_1 = __importDefault(require("../logger"));
const events_1 = require("../models/events");
const wallet_1 = require("../models/wallet");
const auth_1 = require("../utils/auth");
const config_1 = require("./config");
const event_1 = require("./event");
function SanitizeWallet(x) {
    const sanitized = Object.assign({}, x, { wallets: new Array() });
    if (sanitized.wallets && sanitized.wallets.length > 0) {
        delete sanitized.wallets;
    }
    return sanitized;
}
let SocketService = class SocketService {
    constructor({ eventService = event_1.Event, eventModel = events_1.EventStorage, configService = config_1.Config } = {}) {
        this.id = Math.random();
        this.stopped = true;
        this.eventService = eventService;
        this.configService = configService;
        this.serviceConfig = this.configService.for('socket');
        this.eventModel = eventModel;
        this.wireup = this.wireup.bind(this);
        this.start = this.start.bind(this);
        this.signalTx = this.signalTx.bind(this);
        this.signalBlock = this.signalBlock.bind(this);
        this.signalAddressCoin = this.signalAddressCoin.bind(this);
    }
    validateRequest(payload) {
        try {
            const valid = auth_1.Auth.verifyRequestSignature(payload);
            return valid;
        }
        catch {
            return false;
        }
    }
    start({ server }) {
        const bwsKeys = this.serviceConfig.bwsKeys;
        if (this.configService.isDisabled('socket')) {
            logger_1.default.info('Disabled Socket Service');
            return;
        }
        if (this.stopped) {
            this.stopped = false;
            logger_1.default.info('Starting Socket Service');
            this.httpServer = server;
            this.io = new socket_io_1.default.Server(server);
            this.io.sockets.on('connection', socket => {
                socket.on('room', (room, payload) => {
                    const chainNetwork = room.slice(0, room.lastIndexOf('/') + 1);
                    const roomName = room.slice(room.lastIndexOf('/') + 1);
                    switch (roomName) {
                        case 'wallets':
                            if (bwsKeys.includes(payload.pubKey) && this.validateRequest(payload)) {
                                socket.join(room);
                            }
                            else {
                                socket.emit('failure', { message: 'Authentication failed' });
                            }
                            break;
                        case 'wallet':
                            if (this.validateRequest(payload)) {
                                socket.join(chainNetwork + payload.pubKey);
                            }
                            else {
                                socket.emit('failure', { message: 'Authentication failed' });
                            }
                            break;
                        case 'inv':
                        case 'address':
                            socket.join(room);
                            break;
                    }
                });
            });
        }
        this.wireup();
        logger_1.default.info('Started Socket Service');
    }
    async stop() {
        logger_1.default.info('Stopping Socket Service');
        this.stopped = true;
        this.eventService.txEvent.removeAllListeners();
        this.eventService.blockEvent.removeAllListeners();
        this.eventService.addressCoinEvent.removeAllListeners();
    }
    async wireup() {
        this.eventService.txEvent.on('tx', async (tx) => {
            try {
                if (!this.stopped && this.io) {
                    const { chain, network } = tx;
                    const sanitizedTx = SanitizeWallet(tx);
                    this.io.sockets.in(`/${chain}/${network}/inv`).emit('tx', sanitizedTx);
                    if (tx.wallets && tx.wallets.length) {
                        const objectIds = tx.wallets.map(w => new mongodb_1.ObjectID(w));
                        const wallets = await wallet_1.WalletStorage.collection.find({ _id: { $in: objectIds } }).toArray();
                        for (const wallet of wallets) {
                            this.io.sockets.in(`/${chain}/${network}/wallets`).emit('tx', { pubKey: wallet.pubKey, tx });
                            this.io.sockets
                                .in(`/${chain}/${network}/${wallet.pubKey}`)
                                .emit('tx', { pubKey: wallet.pubKey, tx: sanitizedTx });
                        }
                    }
                }
            }
            catch (err) {
                logger_1.default.error('Error in txEvent handler:', err);
            }
        });
        this.eventService.blockEvent.on('block', (block) => {
            if (!this.stopped && this.io) {
                const { chain, network } = block;
                this.io.sockets.in(`/${chain}/${network}/inv`).emit('block', block);
            }
        });
        this.eventService.addressCoinEvent.on('coin', async (addressCoin) => {
            try {
                if (!this.stopped && this.io) {
                    const { coin, address } = addressCoin;
                    const { chain, network } = coin;
                    const sanitizedCoin = SanitizeWallet(coin);
                    this.io.sockets.in(`/${chain}/${network}/address`).emit(address, sanitizedCoin);
                    this.io.sockets.in(`/${chain}/${network}/inv`).emit('coin', sanitizedCoin);
                    if (coin.wallets && coin.wallets.length) {
                        const objectIds = coin.wallets.map(w => new mongodb_1.ObjectID(w));
                        const wallets = await wallet_1.WalletStorage.collection.find({ _id: { $in: objectIds } }).toArray();
                        for (const wallet of wallets) {
                            this.io.sockets.in(`/${chain}/${network}/wallets`).emit('coin', { pubKey: wallet.pubKey, coin });
                            this.io.sockets
                                .in(`/${chain}/${network}/${wallet.pubKey}`)
                                .emit('coin', { pubKey: wallet.pubKey, coin: sanitizedCoin });
                        }
                    }
                }
            }
            catch (err) {
                logger_1.default.error('Error in addressCoinEvent handler:', err);
            }
        });
    }
    async signalBlock(block) {
        await events_1.EventStorage.signalBlock(block);
    }
    async signalTx(tx) {
        await events_1.EventStorage.signalTx(tx);
    }
    async signalAddressCoin(payload) {
        await events_1.EventStorage.signalAddressCoin(payload);
    }
};
exports.SocketService = SocketService;
exports.SocketService = SocketService = __decorate([
    Loggify_1.LoggifyClass
], SocketService);
exports.Socket = new SocketService();
//# sourceMappingURL=socket.js.map