"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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkCollector = void 0;
const si = __importStar(require("systeminformation"));
const logger_1 = require("../logger");
class NetworkCollector {
    constructor() {
        this.trafficRules = [];
        this.urlFilters = [];
        this.blockedConnections = [];
    }
    async collectConnections() {
        try {
            const connections = await si.networkConnections();
            return connections.map(c => ({
                protocol: c.protocol,
                localAddress: c.localAddress,
                localPort: c.localPort ? parseInt(String(c.localPort)) : 0,
                peerAddress: c.peerAddress,
                peerPort: c.peerPort ? parseInt(String(c.peerPort)) : 0,
                state: c.state,
                process: c.process || undefined,
                pid: c.pid || undefined,
            }));
        }
        catch (e) {
            logger_1.logger.error('Network collection error:', e);
            return [];
        }
    }
    async collectStats() {
        try {
            const stats = await si.networkStats();
            return stats.map(s => ({
                interface: s.iface,
                rxBytes: s.rx_bytes,
                txBytes: s.tx_bytes,
                rxDropped: s.rx_dropped,
                txDropped: s.tx_dropped,
                rxErrors: s.rx_errors,
                txErrors: s.tx_errors,
                rxSec: s.rx_sec,
                txSec: s.tx_sec,
            }));
        }
        catch (e) {
            logger_1.logger.error('Network stats error:', e);
            return [];
        }
    }
    updateTrafficRules(rules) {
        this.trafficRules = rules;
        logger_1.logger.info(`Updated ${rules.length} traffic rules`);
    }
    updateURLFilters(filters) {
        this.urlFilters = filters;
        logger_1.logger.info(`Updated ${filters.length} URL filters`);
    }
    getURLFilters() {
        return [...this.urlFilters];
    }
    // Evaluate a connection against traffic rules
    evaluateConnection(conn) {
        const sortedRules = [...this.trafficRules]
            .filter(r => r.enabled)
            .sort((a, b) => a.priority - b.priority);
        for (const rule of sortedRules) {
            if (this.matchesRule(conn, rule)) {
                return { action: rule.action, rule };
            }
        }
        return { action: 'allow' };
    }
    // Evaluate a URL against URL filters
    evaluateURL(url) {
        for (const filter of this.urlFilters.filter(f => f.enabled)) {
            if (!this.isTimeConditionActive(filter.timeCondition))
                continue;
            if (!this.matchesUrlFilter(url, filter))
                continue;
            const fallbackAction = (() => {
                const t = String(filter.type || '').toLowerCase();
                if (t === 'allow' || t === 'log' || t === 'block')
                    return t;
                return 'block';
            })();
            return {
                action: String(filter.action || fallbackAction),
                filter,
                category: filter.category,
            };
        }
        return { action: 'allow' };
    }
    escapeRegex(value) {
        return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }
    parseTimeToMinutes(value) {
        const match = String(value || '').trim().match(/^(\d{1,2}):(\d{2})$/);
        if (!match)
            return null;
        const hours = Number(match[1]);
        const minutes = Number(match[2]);
        if (!Number.isFinite(hours) || !Number.isFinite(minutes))
            return null;
        if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59)
            return null;
        return (hours * 60) + minutes;
    }
    isTimeConditionActive(condition) {
        if (!condition || typeof condition !== 'object')
            return true;
        if (condition.allDay !== false && !condition.startTime && !condition.endTime && !Array.isArray(condition.days)) {
            return true;
        }
        const now = new Date();
        if (Array.isArray(condition.days) && condition.days.length > 0) {
            const allowedDays = condition.days
                .map((day) => Number(day))
                .filter((day) => Number.isFinite(day) && day >= 0 && day <= 6);
            if (allowedDays.length > 0 && !allowedDays.includes(now.getDay())) {
                return false;
            }
        }
        if (condition.allDay !== false) {
            return true;
        }
        const startMinutes = this.parseTimeToMinutes(condition.startTime);
        const endMinutes = this.parseTimeToMinutes(condition.endTime);
        if (startMinutes === null || endMinutes === null)
            return true;
        if (startMinutes === endMinutes)
            return true;
        const currentMinutes = (now.getHours() * 60) + now.getMinutes();
        if (startMinutes < endMinutes) {
            return currentMinutes >= startMinutes && currentMinutes <= endMinutes;
        }
        return currentMinutes >= startMinutes || currentMinutes <= endMinutes;
    }
    wildcardToRegex(pattern) {
        try {
            const source = `^${String(pattern || '').split('*').map((part) => this.escapeRegex(part)).join('.*')}$`;
            return new RegExp(source, 'i');
        }
        catch {
            return null;
        }
    }
    wildcardMatches(value, pattern) {
        const rx = this.wildcardToRegex(pattern);
        if (!rx)
            return false;
        return rx.test(String(value || ''));
    }
    matchesUrlFilter(url, filter) {
        const pattern = String(filter.pattern || '').trim();
        if (!pattern)
            return false;
        const mode = String(filter.matchType || filter.type || 'domain').toLowerCase();
        const lowerPattern = pattern.toLowerCase();
        const lowerUrl = String(url || '').toLowerCase();
        if (!lowerUrl)
            return false;
        if (mode === 'regex') {
            try {
                return new RegExp(pattern, 'i').test(url);
            }
            catch {
                return false;
            }
        }
        try {
            const parsed = new URL(/^https?:\/\//i.test(url) ? url : `https://${url}`);
            const host = parsed.hostname.toLowerCase();
            if (mode === 'domain') {
                if (lowerPattern === '*')
                    return true;
                const normalizedPattern = lowerPattern.startsWith('*.') ? lowerPattern.slice(2) : lowerPattern;
                if (normalizedPattern && (host === normalizedPattern || host.endsWith(`.${normalizedPattern}`))) {
                    return true;
                }
                if (lowerPattern.includes('*') && this.wildcardMatches(host, lowerPattern)) {
                    return true;
                }
                return normalizedPattern ? lowerUrl.includes(normalizedPattern) : false;
            }
            if (mode === 'exact') {
                if (lowerPattern.includes('*')) {
                    return this.wildcardMatches(lowerUrl, lowerPattern) || this.wildcardMatches(host, lowerPattern);
                }
                return lowerUrl === lowerPattern || host === lowerPattern;
            }
            if (mode === 'url' && lowerPattern.includes('*')) {
                return this.wildcardMatches(lowerUrl, lowerPattern);
            }
            return lowerUrl.includes(lowerPattern);
        }
        catch {
            return lowerUrl.includes(lowerPattern);
        }
    }
    matchesRule(conn, rule) {
        return rule.conditions.every(cond => {
            const value = this.getFieldValue(conn, cond.field);
            switch (cond.operator) {
                case 'equals': return value === cond.value;
                case 'not_equals': return value !== cond.value;
                case 'contains': return String(value).includes(cond.value);
                case 'starts_with': return String(value).startsWith(cond.value);
                case 'regex': try {
                    return new RegExp(cond.value).test(String(value));
                }
                catch {
                    return false;
                }
                case 'in_range': {
                    const [start, end] = cond.value.split('-').map(Number);
                    const num = Number(value);
                    return num >= start && num <= end;
                }
                case 'in_subnet': return this.isInSubnet(String(value), cond.value);
                default: return false;
            }
        });
    }
    getFieldValue(conn, field) {
        switch (field) {
            case 'src_ip': return conn.localAddress;
            case 'dst_ip': return conn.peerAddress;
            case 'src_port': return conn.localPort;
            case 'dst_port': return conn.peerPort;
            case 'protocol': return conn.protocol;
            case 'state': return conn.state;
            case 'process': return conn.process || '';
            default: return '';
        }
    }
    isInSubnet(ip, cidr) {
        try {
            const [subnet, bits] = cidr.split('/');
            const mask = ~(2 ** (32 - parseInt(bits)) - 1);
            const ipNum = ip.split('.').reduce((acc, oct) => (acc << 8) + parseInt(oct), 0);
            const subnetNum = subnet.split('.').reduce((acc, oct) => (acc << 8) + parseInt(oct), 0);
            return (ipNum & mask) === (subnetNum & mask);
        }
        catch {
            return false;
        }
    }
    getBlockedConnections() {
        const blocked = [...this.blockedConnections];
        this.blockedConnections = [];
        return blocked;
    }
}
exports.NetworkCollector = NetworkCollector;
//# sourceMappingURL=network.js.map