"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProbeRunner = void 0;
const child_process_1 = require("child_process");
class ProbeRunner {
    async runPing(target, count = 10, timeout = 5000) {
        const platform = process.platform;
        return new Promise((resolve, reject) => {
            let cmd;
            let args;
            if (platform === 'win32') {
                cmd = 'ping';
                args = ['-n', String(count), '-w', String(Math.floor(timeout)), target];
            }
            else {
                cmd = 'ping';
                args = ['-c', String(count), '-W', String(Math.floor(timeout / 1000)), target];
            }
            const startTime = Date.now();
            let output = '';
            const proc = (0, child_process_1.spawn)(cmd, args, { timeout: (count * timeout) + 5000 });
            proc.stdout.on('data', (data) => { output += data.toString(); });
            proc.stderr.on('data', (data) => { output += data.toString(); });
            proc.on('close', (code) => {
                try {
                    const result = this.parsePingOutput(output, target, platform);
                    resolve(result);
                }
                catch (e) {
                    reject(new Error(`Ping parse error: ${e}`));
                }
            });
            proc.on('error', (e) => {
                reject(new Error(`Ping failed: ${e.message}`));
            });
        });
    }
    parsePingOutput(output, target, platform) {
        const replies = [];
        const lines = output.split('\n');
        // Parse individual replies
        for (const line of lines) {
            if (platform === 'win32') {
                const match = line.match(/Reply from .+: bytes=\d+ time[=<](\d+)ms TTL=(\d+)/i);
                if (match) {
                    replies.push({
                        seq: replies.length + 1,
                        time: parseInt(match[1]),
                        ttl: parseInt(match[2]),
                    });
                }
            }
            else {
                const match = line.match(/icmp_seq=(\d+) ttl=(\d+) time=([0-9.]+)/);
                if (match) {
                    replies.push({
                        seq: parseInt(match[1]),
                        ttl: parseInt(match[2]),
                        time: parseFloat(match[3]),
                    });
                }
            }
        }
        // Parse summary
        let sent = 0, received = 0, lost = 0, minRtt = 0, avgRtt = 0, maxRtt = 0;
        if (platform === 'win32') {
            const statsMatch = output.match(/Sent = (\d+), Received = (\d+), Lost = (\d+)/);
            if (statsMatch) {
                sent = parseInt(statsMatch[1]);
                received = parseInt(statsMatch[2]);
                lost = parseInt(statsMatch[3]);
            }
            const rttMatch = output.match(/Minimum = (\d+)ms, Maximum = (\d+)ms, Average = (\d+)ms/);
            if (rttMatch) {
                minRtt = parseInt(rttMatch[1]);
                maxRtt = parseInt(rttMatch[2]);
                avgRtt = parseInt(rttMatch[3]);
            }
        }
        else {
            const statsMatch = output.match(/(\d+) packets transmitted, (\d+) (?:packets )?received/);
            if (statsMatch) {
                sent = parseInt(statsMatch[1]);
                received = parseInt(statsMatch[2]);
                lost = sent - received;
            }
            const rttMatch = output.match(/(?:rtt|round-trip) min\/avg\/max\/(?:mdev|stddev) = ([0-9.]+)\/([0-9.]+)\/([0-9.]+)/);
            if (rttMatch) {
                minRtt = parseFloat(rttMatch[1]);
                avgRtt = parseFloat(rttMatch[2]);
                maxRtt = parseFloat(rttMatch[3]);
            }
        }
        // Calculate jitter from reply times
        let jitter = 0;
        if (replies.length > 1) {
            const diffs = [];
            for (let i = 1; i < replies.length; i++) {
                diffs.push(Math.abs(replies[i].time - replies[i - 1].time));
            }
            jitter = diffs.reduce((a, b) => a + b, 0) / diffs.length;
        }
        return {
            target,
            sent: sent || replies.length,
            received: received || replies.length,
            lost,
            lossPercent: sent > 0 ? (lost / sent) * 100 : 0,
            minRtt: minRtt || Math.min(...replies.map(r => r.time), 0),
            avgRtt: avgRtt || (replies.length ? replies.reduce((a, r) => a + r.time, 0) / replies.length : 0),
            maxRtt: maxRtt || Math.max(...replies.map(r => r.time), 0),
            jitter,
            timestamp: new Date().toISOString(),
            replies,
        };
    }
    async runMTR(target, count = 10, timeout = 30000) {
        const platform = process.platform;
        if (platform === 'win32') {
            return this.runTracerouteWindows(target, timeout);
        }
        return new Promise((resolve, reject) => {
            // Try mtr first, fall back to traceroute
            const cmd = 'mtr';
            const args = ['--report', '--report-cycles', String(count), '--json', target];
            const proc = (0, child_process_1.spawn)(cmd, args, { timeout });
            let output = '';
            proc.stdout.on('data', (data) => { output += data.toString(); });
            proc.stderr.on('data', () => { });
            proc.on('close', (code) => {
                try {
                    if (code === 0 && output.trim()) {
                        const result = this.parseMTRJson(output, target);
                        resolve(result);
                    }
                    else {
                        // Fall back to traceroute
                        this.runTracerouteUnix(target, timeout).then(resolve).catch(reject);
                    }
                }
                catch (e) {
                    this.runTracerouteUnix(target, timeout).then(resolve).catch(reject);
                }
            });
            proc.on('error', () => {
                this.runTracerouteUnix(target, timeout).then(resolve).catch(reject);
            });
        });
    }
    parseMTRJson(output, target) {
        const data = JSON.parse(output);
        const hubs = data.report?.hubs || [];
        return {
            target,
            hopCount: hubs.length,
            timestamp: new Date().toISOString(),
            hops: hubs.map((h, i) => ({
                hop: i + 1,
                host: h.host || '*',
                ip: h.host || '*',
                loss: h.Loss || 0,
                sent: h.Snt || 0,
                received: (h.Snt || 0) - (h.Drop || 0),
                best: h.Best || 0,
                avg: h.Avg || 0,
                worst: h.Wrst || 0,
                stdev: h.StDev || 0,
            })),
        };
    }
    runTracerouteWindows(target, timeout) {
        return new Promise((resolve, reject) => {
            (0, child_process_1.exec)(`tracert -d -w 2000 ${target}`, { timeout }, (err, stdout) => {
                const hops = [];
                const lines = stdout?.split('\n') || [];
                for (const line of lines) {
                    const match = line.match(/^\s*(\d+)\s+(?:(\d+)\s*ms\s+|[*]\s+)(?:(\d+)\s*ms\s+|[*]\s+)(?:(\d+)\s*ms\s+|[*]\s+)(.+)/);
                    if (match) {
                        const times = [match[2], match[3], match[4]].filter(t => t).map(Number);
                        hops.push({
                            hop: parseInt(match[1]),
                            host: match[5]?.trim() || '*',
                            ip: match[5]?.trim() || '*',
                            loss: times.length < 3 ? ((3 - times.length) / 3) * 100 : 0,
                            sent: 3,
                            received: times.length,
                            best: times.length ? Math.min(...times) : 0,
                            avg: times.length ? times.reduce((a, b) => a + b, 0) / times.length : 0,
                            worst: times.length ? Math.max(...times) : 0,
                            stdev: 0,
                        });
                    }
                }
                resolve({
                    target,
                    hopCount: hops.length,
                    timestamp: new Date().toISOString(),
                    hops,
                });
            });
        });
    }
    runTracerouteUnix(target, timeout) {
        return new Promise((resolve, reject) => {
            (0, child_process_1.exec)(`traceroute -n -w 2 ${target}`, { timeout }, (err, stdout) => {
                const hops = [];
                const lines = stdout?.split('\n') || [];
                for (const line of lines) {
                    const match = line.match(/^\s*(\d+)\s+(\S+)\s+([0-9.]+)\s*ms/);
                    if (match) {
                        hops.push({
                            hop: parseInt(match[1]),
                            host: match[2],
                            ip: match[2],
                            loss: 0,
                            sent: 3,
                            received: 3,
                            best: parseFloat(match[3]),
                            avg: parseFloat(match[3]),
                            worst: parseFloat(match[3]),
                            stdev: 0,
                        });
                    }
                }
                resolve({
                    target,
                    hopCount: hops.length,
                    timestamp: new Date().toISOString(),
                    hops,
                });
            });
        });
    }
}
exports.ProbeRunner = ProbeRunner;
//# sourceMappingURL=probe-runner.js.map