import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { logger } from '../logger.js';
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from './classes.securitylogger.js';
import { RustSecurityBridge } from './classes.rustsecuritybridge.js';
import { LRUCache } from 'lru-cache';
/**
 * Reputation threshold scores
 */
export var ReputationThreshold;
(function (ReputationThreshold) {
    ReputationThreshold[ReputationThreshold["HIGH_RISK"] = 20] = "HIGH_RISK";
    ReputationThreshold[ReputationThreshold["MEDIUM_RISK"] = 50] = "MEDIUM_RISK";
    ReputationThreshold[ReputationThreshold["LOW_RISK"] = 80] = "LOW_RISK"; // Score below this is considered low risk (but not trusted)
})(ReputationThreshold || (ReputationThreshold = {}));
/**
 * IP type classifications
 */
export var IPType;
(function (IPType) {
    IPType["RESIDENTIAL"] = "residential";
    IPType["DATACENTER"] = "datacenter";
    IPType["PROXY"] = "proxy";
    IPType["TOR"] = "tor";
    IPType["VPN"] = "vpn";
    IPType["UNKNOWN"] = "unknown";
})(IPType || (IPType = {}));
/**
 * Class for checking IP reputation of inbound email senders
 */
export class IPReputationChecker {
    static instance;
    reputationCache;
    options;
    storageManager; // StorageManager instance
    // Default DNSBL servers
    static DEFAULT_DNSBL_SERVERS = [
        'zen.spamhaus.org', // Spamhaus
        'bl.spamcop.net', // SpamCop
        'b.barracudacentral.org', // Barracuda
        'spam.dnsbl.sorbs.net', // SORBS
        'dnsbl.sorbs.net', // SORBS (expanded)
        'cbl.abuseat.org', // Composite Blocking List 
        'xbl.spamhaus.org', // Spamhaus XBL
        'pbl.spamhaus.org', // Spamhaus PBL
        'dnsbl-1.uceprotect.net', // UCEPROTECT
        'psbl.surriel.com' // PSBL
    ];
    // Default options
    static DEFAULT_OPTIONS = {
        maxCacheSize: 10000,
        cacheTTL: 24 * 60 * 60 * 1000, // 24 hours
        dnsblServers: IPReputationChecker.DEFAULT_DNSBL_SERVERS,
        highRiskThreshold: ReputationThreshold.HIGH_RISK,
        mediumRiskThreshold: ReputationThreshold.MEDIUM_RISK,
        lowRiskThreshold: ReputationThreshold.LOW_RISK,
        enableLocalCache: true,
        enableDNSBL: true,
        enableIPInfo: true
    };
    /**
     * Constructor for IPReputationChecker
     * @param options Configuration options
     * @param storageManager Optional StorageManager instance for persistence
     */
    constructor(options = {}, storageManager) {
        // Merge with default options
        this.options = {
            ...IPReputationChecker.DEFAULT_OPTIONS,
            ...options
        };
        this.storageManager = storageManager;
        // If no storage manager provided, log warning
        if (!storageManager && this.options.enableLocalCache) {
            logger.log('warn', '⚠️  WARNING: IPReputationChecker initialized without StorageManager.\n' +
                '   IP reputation cache will only be stored to filesystem.\n' +
                '   Consider passing a StorageManager instance for better storage flexibility.');
        }
        // Initialize reputation cache
        this.reputationCache = new LRUCache({
            max: this.options.maxCacheSize,
            ttl: this.options.cacheTTL, // Cache TTL
        });
        // Load cache from disk if enabled
        if (this.options.enableLocalCache) {
            // Fire and forget the load operation
            this.loadCache().catch(error => {
                logger.log('error', `Failed to load IP reputation cache during initialization: ${error.message}`);
            });
        }
    }
    /**
     * Get the singleton instance of the checker
     * @param options Configuration options
     * @param storageManager Optional StorageManager instance for persistence
     * @returns Singleton instance
     */
    static getInstance(options = {}, storageManager) {
        if (!IPReputationChecker.instance) {
            IPReputationChecker.instance = new IPReputationChecker(options, storageManager);
        }
        return IPReputationChecker.instance;
    }
    /**
     * Check an IP address's reputation
     * @param ip IP address to check
     * @returns Reputation check result
     */
    async checkReputation(ip) {
        try {
            // Validate IP address format
            if (!this.isValidIPAddress(ip)) {
                logger.log('warn', `Invalid IP address format: ${ip}`);
                return this.createErrorResult(ip, 'Invalid IP address format');
            }
            // Check cache first
            const cachedResult = this.reputationCache.get(ip);
            if (cachedResult) {
                logger.log('info', `Using cached reputation data for IP ${ip}`, {
                    score: cachedResult.score,
                    isSpam: cachedResult.isSpam
                });
                return cachedResult;
            }
            // Try Rust bridge first (parallel DNSBL via tokio — faster than Node sequential DNS)
            const bridge = RustSecurityBridge.getInstance();
            if (bridge.running) {
                try {
                    const rustResult = await bridge.checkIpReputation(ip);
                    const result = {
                        score: rustResult.score,
                        isSpam: rustResult.listed_count > 0,
                        isProxy: rustResult.ip_type === 'proxy',
                        isTor: rustResult.ip_type === 'tor',
                        isVPN: rustResult.ip_type === 'vpn',
                        blacklists: rustResult.dnsbl_results
                            .filter(d => d.listed)
                            .map(d => d.server),
                        timestamp: Date.now(),
                    };
                    this.reputationCache.set(ip, result);
                    if (this.options.enableLocalCache) {
                        this.saveCache().catch(error => {
                            logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
                        });
                    }
                    this.logReputationCheck(ip, result);
                    return result;
                }
                catch (err) {
                    logger.log('warn', `Rust IP reputation check failed, falling back to TS: ${err.message}`);
                }
            }
            // Fallback: TypeScript DNSBL implementation
            const result = {
                score: 100, // Start with perfect score
                isSpam: false,
                isProxy: false,
                isTor: false,
                isVPN: false,
                timestamp: Date.now()
            };
            // Check IP against DNS blacklists if enabled
            if (this.options.enableDNSBL) {
                const dnsblResult = await this.checkDNSBL(ip);
                // Update result with DNSBL information
                result.score -= dnsblResult.listCount * 10; // Subtract 10 points per blacklist
                result.isSpam = dnsblResult.listCount > 0;
                result.blacklists = dnsblResult.lists;
            }
            // Get additional IP information if enabled
            if (this.options.enableIPInfo) {
                const ipInfo = await this.getIPInfo(ip);
                // Update result with IP info
                result.country = ipInfo.country;
                result.asn = ipInfo.asn;
                result.org = ipInfo.org;
                // Adjust score based on IP type
                if (ipInfo.type === IPType.PROXY || ipInfo.type === IPType.TOR || ipInfo.type === IPType.VPN) {
                    result.score -= 30; // Subtract 30 points for proxies, Tor, VPNs
                    // Set proxy flags
                    result.isProxy = ipInfo.type === IPType.PROXY;
                    result.isTor = ipInfo.type === IPType.TOR;
                    result.isVPN = ipInfo.type === IPType.VPN;
                }
            }
            // Ensure score is between 0 and 100
            result.score = Math.max(0, Math.min(100, result.score));
            // Update cache with result
            this.reputationCache.set(ip, result);
            // Save cache if enabled
            if (this.options.enableLocalCache) {
                // Fire and forget the save operation
                this.saveCache().catch(error => {
                    logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
                });
            }
            // Log the reputation check
            this.logReputationCheck(ip, result);
            return result;
        }
        catch (error) {
            logger.log('error', `Error checking IP reputation for ${ip}: ${error.message}`, {
                ip,
                stack: error.stack
            });
            return this.createErrorResult(ip, error.message);
        }
    }
    /**
     * Check an IP against DNS blacklists
     * @param ip IP address to check
     * @returns DNSBL check results
     */
    async checkDNSBL(ip) {
        try {
            // Reverse the IP for DNSBL queries
            const reversedIP = this.reverseIP(ip);
            const results = await Promise.allSettled(this.options.dnsblServers.map(async (server) => {
                try {
                    const lookupDomain = `${reversedIP}.${server}`;
                    await plugins.dns.promises.resolve(lookupDomain);
                    return server; // IP is listed in this DNSBL
                }
                catch (error) {
                    if (error.code === 'ENOTFOUND') {
                        return null; // IP is not listed in this DNSBL
                    }
                    throw error; // Other error
                }
            }));
            // Extract successful lookups (listed in DNSBL)
            const lists = results
                .filter((result) => result.status === 'fulfilled' && result.value !== null)
                .map(result => result.value);
            return {
                listCount: lists.length,
                lists
            };
        }
        catch (error) {
            logger.log('error', `Error checking DNSBL for ${ip}: ${error.message}`);
            return {
                listCount: 0,
                lists: []
            };
        }
    }
    /**
     * Get information about an IP address
     * @param ip IP address to check
     * @returns IP information
     */
    async getIPInfo(ip) {
        try {
            // In a real implementation, this would use an IP data service API
            // For this implementation, we'll use a simplified approach
            // Check if it's a known Tor exit node (simplified)
            const isTor = ip.startsWith('171.25.') || ip.startsWith('185.220.') || ip.startsWith('95.216.');
            // Check if it's a known VPN (simplified)
            const isVPN = ip.startsWith('185.156.') || ip.startsWith('37.120.');
            // Check if it's a known proxy (simplified)
            const isProxy = ip.startsWith('34.92.') || ip.startsWith('34.206.');
            // Determine IP type
            let type = IPType.UNKNOWN;
            if (isTor) {
                type = IPType.TOR;
            }
            else if (isVPN) {
                type = IPType.VPN;
            }
            else if (isProxy) {
                type = IPType.PROXY;
            }
            else {
                // Simple datacenters detection (major cloud providers)
                if (ip.startsWith('13.') || // AWS
                    ip.startsWith('35.') || // Google Cloud
                    ip.startsWith('52.') || // AWS
                    ip.startsWith('34.') || // Google Cloud
                    ip.startsWith('104.') // Various providers
                ) {
                    type = IPType.DATACENTER;
                }
                else {
                    type = IPType.RESIDENTIAL;
                }
            }
            // Return the information
            return {
                country: this.determineCountry(ip), // Simplified, would use geolocation service
                asn: 'AS12345', // Simplified, would look up real ASN
                org: this.determineOrg(ip), // Simplified, would use real org data
                type
            };
        }
        catch (error) {
            logger.log('error', `Error getting IP info for ${ip}: ${error.message}`);
            return {
                type: IPType.UNKNOWN
            };
        }
    }
    /**
     * Simplified method to determine country from IP
     * In a real implementation, this would use a geolocation database or service
     * @param ip IP address
     * @returns Country code
     */
    determineCountry(ip) {
        // Simplified mapping for demo purposes
        if (ip.startsWith('13.') || ip.startsWith('52.'))
            return 'US';
        if (ip.startsWith('35.') || ip.startsWith('34.'))
            return 'US';
        if (ip.startsWith('185.'))
            return 'NL';
        if (ip.startsWith('171.'))
            return 'DE';
        return 'XX'; // Unknown
    }
    /**
     * Simplified method to determine organization from IP
     * In a real implementation, this would use an IP-to-org database or service
     * @param ip IP address
     * @returns Organization name
     */
    determineOrg(ip) {
        // Simplified mapping for demo purposes
        if (ip.startsWith('13.') || ip.startsWith('52.'))
            return 'Amazon AWS';
        if (ip.startsWith('35.') || ip.startsWith('34.'))
            return 'Google Cloud';
        if (ip.startsWith('185.156.'))
            return 'NordVPN';
        if (ip.startsWith('37.120.'))
            return 'ExpressVPN';
        if (ip.startsWith('185.220.'))
            return 'Tor Exit Node';
        return 'Unknown';
    }
    /**
     * Reverse an IP address for DNSBL lookups (e.g., 1.2.3.4 -> 4.3.2.1)
     * @param ip IP address to reverse
     * @returns Reversed IP for DNSBL queries
     */
    reverseIP(ip) {
        return ip.split('.').reverse().join('.');
    }
    /**
     * Create an error result for when reputation check fails
     * @param ip IP address
     * @param errorMessage Error message
     * @returns Error result
     */
    createErrorResult(ip, errorMessage) {
        return {
            score: 50, // Neutral score for errors
            isSpam: false,
            isProxy: false,
            isTor: false,
            isVPN: false,
            timestamp: Date.now(),
            error: errorMessage
        };
    }
    /**
     * Validate IP address format
     * @param ip IP address to validate
     * @returns Whether the IP is valid
     */
    isValidIPAddress(ip) {
        // IPv4 regex pattern
        const ipv4Pattern = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
        return ipv4Pattern.test(ip);
    }
    /**
     * Log reputation check to security logger
     * @param ip IP address
     * @param result Reputation result
     */
    logReputationCheck(ip, result) {
        // Determine log level based on reputation score
        let logLevel = SecurityLogLevel.INFO;
        if (result.score < this.options.highRiskThreshold) {
            logLevel = SecurityLogLevel.WARN;
        }
        else if (result.score < this.options.mediumRiskThreshold) {
            logLevel = SecurityLogLevel.INFO;
        }
        // Log the check
        SecurityLogger.getInstance().logEvent({
            level: logLevel,
            type: SecurityEventType.IP_REPUTATION,
            message: `IP reputation check ${result.isSpam ? 'flagged spam' : 'completed'} for ${ip}`,
            ipAddress: ip,
            details: {
                score: result.score,
                isSpam: result.isSpam,
                isProxy: result.isProxy,
                isTor: result.isTor,
                isVPN: result.isVPN,
                country: result.country,
                blacklists: result.blacklists
            },
            success: !result.isSpam
        });
    }
    /**
     * Save cache to disk or storage manager
     */
    async saveCache() {
        try {
            // Convert cache entries to serializable array
            const entries = Array.from(this.reputationCache.entries()).map(([ip, data]) => ({
                ip,
                data
            }));
            // Only save if we have entries
            if (entries.length === 0) {
                return;
            }
            const cacheData = JSON.stringify(entries);
            // Save to storage manager if available
            if (this.storageManager) {
                await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
                logger.log('info', `Saved ${entries.length} IP reputation cache entries to StorageManager`);
            }
            else {
                // Fall back to filesystem
                const cacheDir = plugins.path.join(paths.dataDir, 'security');
                await plugins.smartfs.directory(cacheDir).recursive().create();
                const cacheFile = plugins.path.join(cacheDir, 'ip_reputation_cache.json');
                await plugins.smartfs.file(cacheFile).write(cacheData);
                logger.log('info', `Saved ${entries.length} IP reputation cache entries to disk`);
            }
        }
        catch (error) {
            logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
        }
    }
    /**
     * Load cache from disk or storage manager
     */
    async loadCache() {
        try {
            let cacheData = null;
            let fromFilesystem = false;
            // Try to load from storage manager first
            if (this.storageManager) {
                try {
                    cacheData = await this.storageManager.get('/security/ip-reputation-cache.json');
                    if (!cacheData) {
                        // Check if data exists in filesystem and migrate it
                        const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
                        if (plugins.fs.existsSync(cacheFile)) {
                            logger.log('info', 'Migrating IP reputation cache from filesystem to StorageManager');
                            cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
                            fromFilesystem = true;
                            // Migrate to storage manager
                            await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
                            logger.log('info', 'IP reputation cache migrated to StorageManager successfully');
                            // Optionally delete the old file after successful migration
                            try {
                                plugins.fs.unlinkSync(cacheFile);
                                logger.log('info', 'Old cache file removed after migration');
                            }
                            catch (deleteError) {
                                logger.log('warn', `Could not delete old cache file: ${deleteError.message}`);
                            }
                        }
                    }
                }
                catch (error) {
                    logger.log('error', `Error loading from StorageManager: ${error.message}`);
                }
            }
            else {
                // No storage manager, load from filesystem
                const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
                if (plugins.fs.existsSync(cacheFile)) {
                    cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
                    fromFilesystem = true;
                }
            }
            // Parse and restore cache if data was found
            if (cacheData) {
                const entries = JSON.parse(cacheData);
                // Validate and filter entries
                const now = Date.now();
                const validEntries = entries.filter(entry => {
                    const age = now - entry.data.timestamp;
                    return age < this.options.cacheTTL; // Only load entries that haven't expired
                });
                // Restore cache
                for (const entry of validEntries) {
                    this.reputationCache.set(entry.ip, entry.data);
                }
                const source = fromFilesystem ? 'disk' : 'StorageManager';
                logger.log('info', `Loaded ${validEntries.length} IP reputation cache entries from ${source}`);
            }
        }
        catch (error) {
            logger.log('error', `Failed to load IP reputation cache: ${error.message}`);
        }
    }
    /**
     * Get the risk level for a reputation score
     * @param score Reputation score (0-100)
     * @returns Risk level description
     */
    static getRiskLevel(score) {
        if (score < ReputationThreshold.HIGH_RISK) {
            return 'high';
        }
        else if (score < ReputationThreshold.MEDIUM_RISK) {
            return 'medium';
        }
        else if (score < ReputationThreshold.LOW_RISK) {
            return 'low';
        }
        else {
            return 'trusted';
        }
    }
    /**
     * Update the storage manager after instantiation
     * This is useful when the storage manager is not available at construction time
     * @param storageManager The StorageManager instance to use
     */
    updateStorageManager(storageManager) {
        this.storageManager = storageManager;
        logger.log('info', 'IPReputationChecker storage manager updated');
        // If cache is enabled and we have entries, save them to the new storage manager
        if (this.options.enableLocalCache && this.reputationCache.size > 0) {
            this.saveCache().catch(error => {
                logger.log('error', `Failed to save cache to new storage manager: ${error.message}`);
            });
        }
    }
}
//# sourceMappingURL=data:application/json;base64, 