"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 () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.unsafeUnlock = void 0;
const path_1 = __importDefault(require("path"));
const Context = __importStar(require("../p2p/Context"));
const zlib_1 = __importDefault(require("zlib"));
const trie_prefix_tree_1 = __importDefault(require("trie-prefix-tree"));
const debugMiddleware_1 = require("../network/debugMiddleware");
const nestedCounters_1 = require("../utils/nestedCounters");
const logger_1 = require("../logger");
const ProblemNodeHandler = __importStar(require("../p2p/ProblemNodeHandler"));
const NodeList = __importStar(require("../p2p/NodeList"));
const CycleChain = __importStar(require("../p2p/CycleChain"));
const Context_1 = require("../p2p/Context");
const tar = require('tar-fs');
const fs = require('fs');
exports.unsafeUnlock = false; //REVIEWER WARNING: Never commit or merge this as true.  It disableds endpoint security for internal testing
class Debug {
    constructor(baseDir, network) {
        this.baseDir = baseDir;
        this.network = network;
        this.archiveName = `debug-${network.ipInfo.externalIp}-${network.ipInfo.externalPort}.tar.gz`;
        this.files = {};
        this._registerRoutes();
    }
    addToArchive(src, dest) {
        if (path_1.default.isAbsolute(dest))
            throw new Error('"dest" must be a relative path.');
        src = path_1.default.isAbsolute(src) ? src : path_1.default.resolve(path_1.default.join(this.baseDir, src));
        this.files[src] = dest;
    }
    createArchiveStream() {
        const cwd = process.cwd();
        const filesRel = {};
        for (const src in this.files) {
            const srcRel = path_1.default.relative(cwd, src).replace(/\\/g, '/');
            const dest = this.files[src];
            filesRel[srcRel] = dest;
        }
        const entries = Object.keys(filesRel);
        const trie = (0, trie_prefix_tree_1.default)(entries);
        const pack = tar.pack(cwd, {
            entries,
            map: function (header) {
                let entry = header.name.replace(/\\/g, '/');
                // Find the closest entry for this item
                while (!trie.isPrefix(entry) && entry.length > 0) {
                    entry = entry.slice(0, -1);
                }
                if (entry.length === 0) {
                    if (logger_1.logFlags.error) {
                        nestedCounters_1.nestedCountersInstance.countEvent('debug', 'error: No valid entry found for header');
                        this.main.logger.error('debug: No valid entry found for header:', header.name);
                    }
                    return header;
                }
                // Remove srcRel from header.name
                header.name = path_1.default.relative(entry, header.name);
                // Prefix dest to whatever remains
                const dest = filesRel[entry];
                if (!dest) {
                    if (logger_1.logFlags.error) {
                        nestedCounters_1.nestedCountersInstance.countEvent('debug', 'error: Destination not found for entry');
                        this.main.logger.error('debug: Destination not found for entry:', entry);
                    }
                    return header;
                }
                header.name = path_1.default.normalize(path_1.default.join(dest, header.name));
                return header;
            },
        });
        return pack;
    }
    _registerRoutes() {
        this.network.registerExternalGet('debug', debugMiddleware_1.isDebugModeMiddlewareMedium, (req, res) => {
            const archive = this.createArchiveStream();
            const gzip = zlib_1.default.createGzip();
            res.set('content-disposition', `attachment; filename="${this.archiveName}"`);
            res.set('content-type', 'application/gzip');
            archive.pipe(gzip).pipe(res);
        });
        this.network.registerExternalGet('debug-logfile', debugMiddleware_1.isDebugModeMiddlewareMedium, (req, res) => {
            const requestedFile = req.query.file;
            if (typeof requestedFile !== 'string' || !requestedFile) {
                res.json({ success: false, error: 'Invalid file parameter' });
                return;
            }
            const normalizedFile = path_1.default.normalize(requestedFile).replace(/^(\.\.[/\\])+/, '');
            const logsAbsolutePath = Object.keys(this.files).find((key) => this.files[key] === './logs');
            if (!logsAbsolutePath) {
                res.json({ success: false, error: 'Logs directory not found' });
                return;
            }
            const filePath = path_1.default.join(logsAbsolutePath, normalizedFile);
            if (!filePath.startsWith(logsAbsolutePath)) {
                res.json({ success: false, error: 'File not found' });
                return;
            }
            res.set('Content-Disposition', `attachment; filename="${path_1.default.basename(normalizedFile)}"`);
            res.set('Content-Type', 'text/plain');
            const fileStream = fs.createReadStream(filePath);
            fileStream.on('error', (error) => {
                res.json({ success: false, error: 'Error reading the file' });
                return;
            });
            fileStream.pipe(res);
        });
        this.network.registerExternalGet('debug-network-delay', debugMiddleware_1.isDebugModeMiddleware, (req, res) => {
            try {
                const delay = req.query.delay && typeof req.query.delay === 'string' ? parseInt(req.query.delay) : 120 * 1000;
                this.network.setDebugNetworkDelay(delay);
            }
            catch (e) {
                res.json({ success: false, error: e.message });
                return;
            }
            res.json({ success: true });
            return;
        });
        this.network.registerExternalGet('debug-forcedExpiration', debugMiddleware_1.isDebugModeMiddleware, (req, res) => {
            try {
                const forcedExpiration = req.query.forcedExpiration && typeof req.query.forcedExpiration === 'string'
                    ? req.query.forcedExpiration === 'true'
                    : false;
                Context.config.debug.forcedExpiration = forcedExpiration;
                nestedCounters_1.nestedCountersInstance.countEvent('debug', `forcedExpiration set to ${forcedExpiration}`);
            }
            catch (e) {
                res.json({ success: false, error: e.message });
                return;
            }
            res.json({ success: true });
            return;
        });
        this.network.registerExternalGet('debug_problematicNodeCacheExport', debugMiddleware_1.isDebugModeMiddleware, (_, res) => {
            try {
                const cacheData = ProblemNodeHandler.exportProblematicNodeCache();
                if (!cacheData) {
                    res.json({
                        success: false,
                        error: 'Problematic node cache is not enabled or not available. Ensure enableProblematicNodeCacheBuilding is true.',
                    });
                    return;
                }
                res.json({
                    success: true,
                    data: {
                        compressed: true,
                        cache: cacheData,
                        timestamp: Date.now(),
                    },
                });
            }
            catch (e) {
                res.json({ success: false, error: e.message });
            }
        });
        this.network.registerExternalGet('debug_getProblematicNodes', debugMiddleware_1.isDebugModeMiddleware, (_, res) => {
            try {
                // Check if we have a current cycle
                const currentCycleRecord = CycleChain.newest;
                if (!currentCycleRecord) {
                    res.json({
                        success: false,
                        error: 'No current cycle available',
                    });
                    return;
                }
                // Get problematic nodes using the standard method
                const problematicNodeIds = ProblemNodeHandler.getProblematicNodes(currentCycleRecord);
                // Get detailed info for each problematic node
                const problematicNodesInfo = problematicNodeIds
                    .map((nodeId) => {
                    const node = NodeList.nodes.get(nodeId);
                    if (!node)
                        return null;
                    return {
                        id: nodeId.substring(0, 8),
                        fullId: nodeId,
                        refuteCycles: node.refuteCycles || [],
                        consecutiveRefutes: ProblemNodeHandler.getConsecutiveRefutes(node.refuteCycles || [], currentCycleRecord.counter),
                        refutePercentage: (ProblemNodeHandler.getRefutePercentage(node.refuteCycles || [], currentCycleRecord.counter) * 100).toFixed(1),
                    };
                })
                    .filter((n) => n !== null);
                res.json({
                    success: true,
                    data: {
                        currentCycle: currentCycleRecord.counter,
                        totalActiveNodes: NodeList.activeByIdOrder.length,
                        problematicNodesCount: problematicNodeIds.length,
                        problematicNodes: problematicNodesInfo,
                        evictionEnabled: Context_1.config.p2p.enableProblematicNodeRemoval,
                        thresholds: {
                            consecutiveRefutes: Context_1.config.p2p.problematicNodeConsecutiveRefuteThreshold,
                            refutePercentage: Context_1.config.p2p.problematicNodeRefutePercentageThreshold,
                            historyLength: Context_1.config.p2p.problematicNodeHistoryLength,
                        },
                    },
                });
            }
            catch (e) {
                res.json({ success: false, error: e.message });
            }
        });
        this.network.registerExternalGet('debug_simulateProblematic', debugMiddleware_1.isDebugModeMiddleware, (req, res) => {
            try {
                // Parse parameters with defaults
                const missConsensus = req.query.missConsensus ? parseFloat(req.query.missConsensus) : 0;
                const networkDelay = req.query.networkDelay ? parseInt(req.query.networkDelay) : 0;
                const dropMessages = req.query.dropMessages ? parseFloat(req.query.dropMessages) : 0;
                const slowResponse = req.query.slowResponse ? parseFloat(req.query.slowResponse) : 0;
                const slowDelayMs = req.query.slowDelayMs ? parseInt(req.query.slowDelayMs) : 0;
                const reset = req.query.reset === 'true';
                // Validate parameters
                if (missConsensus < 0 || missConsensus > 1) {
                    throw new Error('missConsensus must be between 0 and 1');
                }
                if (dropMessages < 0 || dropMessages > 1) {
                    throw new Error('dropMessages must be between 0 and 1');
                }
                if (slowResponse < 0 || slowResponse > 1) {
                    throw new Error('slowResponse must be between 0 and 1');
                }
                if (networkDelay < 0 || networkDelay > 60000) {
                    throw new Error('networkDelay must be between 0 and 60000 ms');
                }
                if (slowDelayMs < 0 || slowDelayMs > 60000) {
                    throw new Error('slowDelayMs must be between 0 and 60000 ms');
                }
                // Apply or reset configurations
                if (reset) {
                    // Reset all problematic simulation settings
                    if (Context.config?.debug) {
                        Context.config.debug.missConsensusChance = 0;
                        Context.config.debug.fakeNetworkDelay = 0;
                        Context.config.debug.dropMessageChance = 0;
                        Context.config.debug.slowResponseChance = 0;
                        Context.config.debug.slowResponseDelay = 0;
                    }
                    // Also reset network delay if it was set
                    if (this.network.setDebugNetworkDelay) {
                        this.network.setDebugNetworkDelay(0);
                    }
                    nestedCounters_1.nestedCountersInstance.countEvent('debug', 'simulateProblematic reset');
                    res.json({
                        success: true,
                        message: 'Problematic simulation settings reset',
                        settings: {
                            missConsensus: 0,
                            networkDelay: 0,
                            dropMessages: 0,
                            slowResponse: 0,
                            slowDelayMs: 0,
                        },
                    });
                }
                else {
                    // Apply new settings
                    if (Context.config?.debug) {
                        Context.config.debug.missConsensusChance = missConsensus;
                        Context.config.debug.fakeNetworkDelay = networkDelay;
                        Context.config.debug.dropMessageChance = dropMessages;
                        Context.config.debug.slowResponseChance = slowResponse;
                        Context.config.debug.slowResponseDelay = slowDelayMs;
                    }
                    else {
                        res.json({ success: false, error: 'Debug configuration not available' });
                        return;
                    }
                    // Set network delay if available
                    if (networkDelay > 0 && this.network.setDebugNetworkDelay) {
                        this.network.setDebugNetworkDelay(networkDelay);
                    }
                    nestedCounters_1.nestedCountersInstance.countEvent('debug', `simulateProblematic configured: miss=${missConsensus}, delay=${networkDelay}, drop=${dropMessages}, slow=${slowResponse}/${slowDelayMs}ms`);
                    res.json({
                        success: true,
                        message: 'Problematic simulation settings applied',
                        settings: {
                            missConsensus,
                            networkDelay,
                            dropMessages,
                            slowResponse,
                            slowDelayMs,
                        },
                    });
                }
            }
            catch (e) {
                res.json({ success: false, error: e.message });
            }
        });
        //NEVER EVER RELEASE THIS... can only uncommment for test branches
        // this.network.registerExternalGet('unsafe_unlock', (req, res) => {
        //   try {
        //     unsafeUnlock = req.query.unlock === 'true' ? true : false
        //   } catch (e) {
        //     res.json({ success: false, error: e.message })
        //     return
        //   }
        //   res.json({ success: true , unsafeUnlock})
        //   return
        // })
    }
}
exports.default = Debug;
//# sourceMappingURL=debug.js.map