import * as plugins from '../../plugins.js';
import * as paths from '../../paths.js';
import { logger } from '../../logger.js';
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js';
import { LRUCache } from 'lru-cache';
/**
 * Bounce types for categorizing the reasons for bounces
 */
export var BounceType;
(function (BounceType) {
    // Hard bounces (permanent failures)
    BounceType["INVALID_RECIPIENT"] = "invalid_recipient";
    BounceType["DOMAIN_NOT_FOUND"] = "domain_not_found";
    BounceType["MAILBOX_FULL"] = "mailbox_full";
    BounceType["MAILBOX_INACTIVE"] = "mailbox_inactive";
    BounceType["BLOCKED"] = "blocked";
    BounceType["SPAM_RELATED"] = "spam_related";
    BounceType["POLICY_RELATED"] = "policy_related";
    // Soft bounces (temporary failures)
    BounceType["SERVER_UNAVAILABLE"] = "server_unavailable";
    BounceType["TEMPORARY_FAILURE"] = "temporary_failure";
    BounceType["QUOTA_EXCEEDED"] = "quota_exceeded";
    BounceType["NETWORK_ERROR"] = "network_error";
    BounceType["TIMEOUT"] = "timeout";
    // Special cases
    BounceType["AUTO_RESPONSE"] = "auto_response";
    BounceType["CHALLENGE_RESPONSE"] = "challenge_response";
    BounceType["UNKNOWN"] = "unknown";
})(BounceType || (BounceType = {}));
/**
 * Hard vs soft bounce classification
 */
export var BounceCategory;
(function (BounceCategory) {
    BounceCategory["HARD"] = "hard";
    BounceCategory["SOFT"] = "soft";
    BounceCategory["AUTO_RESPONSE"] = "auto_response";
    BounceCategory["UNKNOWN"] = "unknown";
})(BounceCategory || (BounceCategory = {}));
/**
 * Email bounce patterns to identify bounce types in SMTP responses and bounce messages
 */
const BOUNCE_PATTERNS = {
    // Hard bounce patterns
    [BounceType.INVALID_RECIPIENT]: [
        /no such user/i,
        /user unknown/i,
        /does not exist/i,
        /invalid recipient/i,
        /unknown recipient/i,
        /no mailbox/i,
        /user not found/i,
        /recipient address rejected/i,
        /550 5\.1\.1/i
    ],
    [BounceType.DOMAIN_NOT_FOUND]: [
        /domain not found/i,
        /unknown domain/i,
        /no such domain/i,
        /host not found/i,
        /domain invalid/i,
        /550 5\.1\.2/i
    ],
    [BounceType.MAILBOX_FULL]: [
        /mailbox full/i,
        /over quota/i,
        /quota exceeded/i,
        /552 5\.2\.2/i
    ],
    [BounceType.MAILBOX_INACTIVE]: [
        /mailbox disabled/i,
        /mailbox inactive/i,
        /account disabled/i,
        /mailbox not active/i,
        /account suspended/i
    ],
    [BounceType.BLOCKED]: [
        /blocked/i,
        /rejected/i,
        /denied/i,
        /blacklisted/i,
        /prohibited/i,
        /refused/i,
        /550 5\.7\./i
    ],
    [BounceType.SPAM_RELATED]: [
        /spam/i,
        /bulk mail/i,
        /content rejected/i,
        /message rejected/i,
        /550 5\.7\.1/i
    ],
    // Soft bounce patterns
    [BounceType.SERVER_UNAVAILABLE]: [
        /server unavailable/i,
        /service unavailable/i,
        /try again later/i,
        /try later/i,
        /451 4\.3\./i,
        /421 4\.3\./i
    ],
    [BounceType.TEMPORARY_FAILURE]: [
        /temporary failure/i,
        /temporary error/i,
        /temporary problem/i,
        /try again/i,
        /451 4\./i
    ],
    [BounceType.QUOTA_EXCEEDED]: [
        /quota temporarily exceeded/i,
        /mailbox temporarily full/i,
        /452 4\.2\.2/i
    ],
    [BounceType.NETWORK_ERROR]: [
        /network error/i,
        /connection error/i,
        /connection timed out/i,
        /routing error/i,
        /421 4\.4\./i
    ],
    [BounceType.TIMEOUT]: [
        /timed out/i,
        /timeout/i,
        /450 4\.4\.2/i
    ],
    // Auto-responses
    [BounceType.AUTO_RESPONSE]: [
        /auto[- ]reply/i,
        /auto[- ]response/i,
        /vacation/i,
        /out of office/i,
        /away from office/i,
        /on vacation/i,
        /automatic reply/i
    ],
    [BounceType.CHALLENGE_RESPONSE]: [
        /challenge[- ]response/i,
        /verify your email/i,
        /confirm your email/i,
        /email verification/i
    ]
};
/**
 * Manager for handling email bounces
 */
export class BounceManager {
    // Retry strategy with exponential backoff
    retryStrategy = {
        maxRetries: 5,
        initialDelay: 15 * 60 * 1000, // 15 minutes
        maxDelay: 24 * 60 * 60 * 1000, // 24 hours
        backoffFactor: 2
    };
    // Store of bounced emails
    bounceStore = [];
    // Cache of recently bounced email addresses to avoid sending to known bad addresses
    bounceCache;
    // Suppression list for addresses that should not receive emails
    suppressionList = new Map();
    storageManager; // StorageManager instance
    constructor(options) {
        // Set retry strategy with defaults
        if (options?.retryStrategy) {
            this.retryStrategy = {
                ...this.retryStrategy,
                ...options.retryStrategy
            };
        }
        // Initialize bounce cache with LRU (least recently used) caching
        this.bounceCache = new LRUCache({
            max: options?.maxCacheSize || 10000,
            ttl: options?.cacheTTL || 30 * 24 * 60 * 60 * 1000, // 30 days default
        });
        // Store storage manager reference
        this.storageManager = options?.storageManager;
        // Load suppression list from storage
        // Note: This is async but we can't await in constructor
        // The suppression list will be loaded asynchronously
        this.loadSuppressionList().catch(error => {
            logger.log('error', `Failed to load suppression list on startup: ${error.message}`);
        });
    }
    /**
     * Process a bounce notification
     * @param bounceData Bounce data to process
     * @returns Processed bounce record
     */
    async processBounce(bounceData) {
        try {
            // Add required fields if missing
            const bounce = {
                id: bounceData.id || plugins.uuid.v4(),
                recipient: bounceData.recipient,
                sender: bounceData.sender,
                domain: bounceData.domain || bounceData.recipient.split('@')[1],
                subject: bounceData.subject,
                bounceType: bounceData.bounceType || BounceType.UNKNOWN,
                bounceCategory: bounceData.bounceCategory || BounceCategory.UNKNOWN,
                timestamp: bounceData.timestamp || Date.now(),
                smtpResponse: bounceData.smtpResponse,
                diagnosticCode: bounceData.diagnosticCode,
                statusCode: bounceData.statusCode,
                headers: bounceData.headers,
                processed: false,
                originalEmailId: bounceData.originalEmailId,
                retryCount: bounceData.retryCount || 0,
                nextRetryTime: bounceData.nextRetryTime
            };
            // Determine bounce type and category if not provided
            if (!bounceData.bounceType || bounceData.bounceType === BounceType.UNKNOWN) {
                const bounceInfo = this.detectBounceType(bounce.smtpResponse || '', bounce.diagnosticCode || '', bounce.statusCode || '');
                bounce.bounceType = bounceInfo.type;
                bounce.bounceCategory = bounceInfo.category;
            }
            // Process the bounce based on category
            switch (bounce.bounceCategory) {
                case BounceCategory.HARD:
                    // Handle hard bounce - add to suppression list
                    await this.handleHardBounce(bounce);
                    break;
                case BounceCategory.SOFT:
                    // Handle soft bounce - schedule retry if eligible
                    await this.handleSoftBounce(bounce);
                    break;
                case BounceCategory.AUTO_RESPONSE:
                    // Handle auto-response - typically no action needed
                    logger.log('info', `Auto-response detected for ${bounce.recipient}`);
                    break;
                default:
                    // Unknown bounce type - log for investigation
                    logger.log('warn', `Unknown bounce type for ${bounce.recipient}`, {
                        bounceType: bounce.bounceType,
                        smtpResponse: bounce.smtpResponse
                    });
                    break;
            }
            // Store the bounce record
            bounce.processed = true;
            this.bounceStore.push(bounce);
            // Update the bounce cache
            this.updateBounceCache(bounce);
            // Log the bounce
            logger.log(bounce.bounceCategory === BounceCategory.HARD ? 'warn' : 'info', `Email bounce processed: ${bounce.bounceCategory} bounce for ${bounce.recipient}`, {
                bounceType: bounce.bounceType,
                domain: bounce.domain,
                category: bounce.bounceCategory
            });
            // Enhanced security logging
            SecurityLogger.getInstance().logEvent({
                level: bounce.bounceCategory === BounceCategory.HARD
                    ? SecurityLogLevel.WARN
                    : SecurityLogLevel.INFO,
                type: SecurityEventType.EMAIL_VALIDATION,
                message: `Email bounce detected: ${bounce.bounceCategory} bounce for recipient`,
                domain: bounce.domain,
                details: {
                    recipient: bounce.recipient,
                    bounceType: bounce.bounceType,
                    smtpResponse: bounce.smtpResponse,
                    diagnosticCode: bounce.diagnosticCode,
                    statusCode: bounce.statusCode
                },
                success: false
            });
            return bounce;
        }
        catch (error) {
            logger.log('error', `Error processing bounce: ${error.message}`, {
                error: error.message,
                bounceData
            });
            throw error;
        }
    }
    /**
     * Process an SMTP failure as a bounce
     * @param recipient Recipient email
     * @param smtpResponse SMTP error response
     * @param options Additional options
     * @returns Processed bounce record
     */
    async processSmtpFailure(recipient, smtpResponse, options = {}) {
        // Create bounce data from SMTP failure
        const bounceData = {
            recipient,
            sender: options.sender || '',
            domain: recipient.split('@')[1],
            smtpResponse,
            statusCode: options.statusCode,
            headers: options.headers,
            originalEmailId: options.originalEmailId,
            timestamp: Date.now()
        };
        // Process as a regular bounce
        return this.processBounce(bounceData);
    }
    /**
     * Process a bounce notification email
     * @param bounceEmail The email containing bounce information
     * @returns Processed bounce record or null if not a bounce
     */
    async processBounceEmail(bounceEmail) {
        try {
            // Check if this is a bounce notification
            const subject = bounceEmail.getSubject();
            const body = bounceEmail.getBody();
            // Check for common bounce notification subject patterns
            const isBounceSubject = /mail delivery|delivery (failed|status|notification)|failure notice|returned mail|undeliverable|delivery problem/i.test(subject);
            if (!isBounceSubject) {
                // Not a bounce notification based on subject
                return null;
            }
            // Extract original recipient from the body or headers
            let recipient = '';
            let originalMessageId = '';
            // Extract recipient from common bounce formats
            const recipientMatch = body.match(/(?:failed recipient|to[:=]\s*|recipient:|delivery failed:)\s*<?([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>?/i);
            if (recipientMatch && recipientMatch[1]) {
                recipient = recipientMatch[1];
            }
            // Extract diagnostic code
            let diagnosticCode = '';
            const diagnosticMatch = body.match(/diagnostic(?:-|\\s+)code:\s*(.+)(?:\n|$)/i);
            if (diagnosticMatch && diagnosticMatch[1]) {
                diagnosticCode = diagnosticMatch[1].trim();
            }
            // Extract SMTP status code
            let statusCode = '';
            const statusMatch = body.match(/status(?:-|\\s+)code:\s*([0-9.]+)/i);
            if (statusMatch && statusMatch[1]) {
                statusCode = statusMatch[1].trim();
            }
            // If recipient not found in standard patterns, try DSN (Delivery Status Notification) format
            if (!recipient) {
                // Look for DSN format with Original-Recipient or Final-Recipient fields
                const originalRecipientMatch = body.match(/original-recipient:.*?([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i);
                const finalRecipientMatch = body.match(/final-recipient:.*?([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i);
                if (originalRecipientMatch && originalRecipientMatch[1]) {
                    recipient = originalRecipientMatch[1];
                }
                else if (finalRecipientMatch && finalRecipientMatch[1]) {
                    recipient = finalRecipientMatch[1];
                }
            }
            // If still no recipient, can't process as bounce
            if (!recipient) {
                logger.log('warn', 'Could not extract recipient from bounce notification', {
                    subject,
                    sender: bounceEmail.from
                });
                return null;
            }
            // Extract original message ID if available
            const messageIdMatch = body.match(/original[ -]message[ -]id:[ \t]*<?([^>]+)>?/i);
            if (messageIdMatch && messageIdMatch[1]) {
                originalMessageId = messageIdMatch[1].trim();
            }
            // Create bounce data
            const bounceData = {
                recipient,
                sender: bounceEmail.from,
                domain: recipient.split('@')[1],
                subject: bounceEmail.getSubject(),
                diagnosticCode,
                statusCode,
                timestamp: Date.now(),
                headers: {}
            };
            // Process as a regular bounce
            return this.processBounce(bounceData);
        }
        catch (error) {
            logger.log('error', `Error processing bounce email: ${error.message}`);
            return null;
        }
    }
    /**
     * Handle a hard bounce by adding to suppression list
     * @param bounce The bounce record
     */
    async handleHardBounce(bounce) {
        // Add to suppression list permanently (no expiry)
        this.addToSuppressionList(bounce.recipient, `Hard bounce: ${bounce.bounceType}`, undefined);
        // Increment bounce count in cache
        this.updateBounceCache(bounce);
        // Save to permanent storage
        await this.saveBounceRecord(bounce);
        // Log hard bounce for monitoring
        logger.log('warn', `Hard bounce for ${bounce.recipient}: ${bounce.bounceType}`, {
            domain: bounce.domain,
            smtpResponse: bounce.smtpResponse,
            diagnosticCode: bounce.diagnosticCode
        });
    }
    /**
     * Handle a soft bounce by scheduling a retry if eligible
     * @param bounce The bounce record
     */
    async handleSoftBounce(bounce) {
        // Check if we've exceeded max retries
        if (bounce.retryCount >= this.retryStrategy.maxRetries) {
            logger.log('warn', `Max retries exceeded for ${bounce.recipient}, treating as hard bounce`);
            // Convert to hard bounce after max retries
            bounce.bounceCategory = BounceCategory.HARD;
            await this.handleHardBounce(bounce);
            return;
        }
        // Calculate next retry time with exponential backoff
        const delay = Math.min(this.retryStrategy.initialDelay * Math.pow(this.retryStrategy.backoffFactor, bounce.retryCount), this.retryStrategy.maxDelay);
        bounce.retryCount++;
        bounce.nextRetryTime = Date.now() + delay;
        // Add to suppression list temporarily (with expiry)
        this.addToSuppressionList(bounce.recipient, `Soft bounce: ${bounce.bounceType}`, bounce.nextRetryTime);
        // Log the retry schedule
        logger.log('info', `Scheduled retry ${bounce.retryCount} for ${bounce.recipient} at ${new Date(bounce.nextRetryTime).toISOString()}`, {
            bounceType: bounce.bounceType,
            retryCount: bounce.retryCount,
            nextRetry: bounce.nextRetryTime
        });
    }
    /**
     * Add an email address to the suppression list
     * @param email Email address to suppress
     * @param reason Reason for suppression
     * @param expiresAt Expiration timestamp (undefined for permanent)
     */
    addToSuppressionList(email, reason, expiresAt) {
        this.suppressionList.set(email.toLowerCase(), {
            reason,
            timestamp: Date.now(),
            expiresAt
        });
        // Save asynchronously without blocking
        this.saveSuppressionList().catch(error => {
            logger.log('error', `Failed to save suppression list after adding ${email}: ${error.message}`);
        });
        logger.log('info', `Added ${email} to suppression list`, {
            reason,
            expiresAt: expiresAt ? new Date(expiresAt).toISOString() : 'permanent'
        });
    }
    /**
     * Remove an email address from the suppression list
     * @param email Email address to remove
     */
    removeFromSuppressionList(email) {
        const wasRemoved = this.suppressionList.delete(email.toLowerCase());
        if (wasRemoved) {
            // Save asynchronously without blocking
            this.saveSuppressionList().catch(error => {
                logger.log('error', `Failed to save suppression list after removing ${email}: ${error.message}`);
            });
            logger.log('info', `Removed ${email} from suppression list`);
        }
    }
    /**
     * Check if an email is on the suppression list
     * @param email Email address to check
     * @returns Whether the email is suppressed
     */
    isEmailSuppressed(email) {
        const lowercaseEmail = email.toLowerCase();
        const suppression = this.suppressionList.get(lowercaseEmail);
        if (!suppression) {
            return false;
        }
        // Check if suppression has expired
        if (suppression.expiresAt && Date.now() > suppression.expiresAt) {
            this.suppressionList.delete(lowercaseEmail);
            // Save asynchronously without blocking
            this.saveSuppressionList().catch(error => {
                logger.log('error', `Failed to save suppression list after expiry cleanup: ${error.message}`);
            });
            return false;
        }
        return true;
    }
    /**
     * Get suppression information for an email
     * @param email Email address to check
     * @returns Suppression information or null if not suppressed
     */
    getSuppressionInfo(email) {
        const lowercaseEmail = email.toLowerCase();
        const suppression = this.suppressionList.get(lowercaseEmail);
        if (!suppression) {
            return null;
        }
        // Check if suppression has expired
        if (suppression.expiresAt && Date.now() > suppression.expiresAt) {
            this.suppressionList.delete(lowercaseEmail);
            // Save asynchronously without blocking
            this.saveSuppressionList().catch(error => {
                logger.log('error', `Failed to save suppression list after expiry cleanup: ${error.message}`);
            });
            return null;
        }
        return suppression;
    }
    /**
     * Save suppression list to disk
     */
    async saveSuppressionList() {
        try {
            const suppressionData = JSON.stringify(Array.from(this.suppressionList.entries()));
            if (this.storageManager) {
                // Use storage manager
                await this.storageManager.set('/email/bounces/suppression-list.json', suppressionData);
            }
            else {
                // Fall back to filesystem
                await plugins.smartfs.file(plugins.path.join(paths.dataDir, 'emails', 'suppression_list.json')).write(suppressionData);
            }
        }
        catch (error) {
            logger.log('error', `Failed to save suppression list: ${error.message}`);
        }
    }
    /**
     * Load suppression list from disk
     */
    async loadSuppressionList() {
        try {
            let entries = null;
            let needsMigration = false;
            if (this.storageManager) {
                // Try to load from storage manager first
                const suppressionData = await this.storageManager.get('/email/bounces/suppression-list.json');
                if (suppressionData) {
                    entries = JSON.parse(suppressionData);
                }
                else {
                    // Check if data exists in filesystem and migrate
                    const suppressionPath = plugins.path.join(paths.dataDir, 'emails', 'suppression_list.json');
                    if (plugins.fs.existsSync(suppressionPath)) {
                        const data = plugins.fs.readFileSync(suppressionPath, 'utf8');
                        entries = JSON.parse(data);
                        needsMigration = true;
                        logger.log('info', 'Migrating suppression list from filesystem to StorageManager');
                    }
                }
            }
            else {
                // No storage manager, use filesystem directly
                const suppressionPath = plugins.path.join(paths.dataDir, 'emails', 'suppression_list.json');
                if (plugins.fs.existsSync(suppressionPath)) {
                    const data = plugins.fs.readFileSync(suppressionPath, 'utf8');
                    entries = JSON.parse(data);
                }
            }
            if (entries) {
                this.suppressionList = new Map(entries);
                // Clean expired entries
                const now = Date.now();
                let expiredCount = 0;
                for (const [email, info] of this.suppressionList.entries()) {
                    if (info.expiresAt && now > info.expiresAt) {
                        this.suppressionList.delete(email);
                        expiredCount++;
                    }
                }
                if (expiredCount > 0 || needsMigration) {
                    logger.log('info', `Cleaned ${expiredCount} expired entries from suppression list`);
                    await this.saveSuppressionList();
                }
                logger.log('info', `Loaded ${this.suppressionList.size} entries from suppression list`);
            }
        }
        catch (error) {
            logger.log('error', `Failed to load suppression list: ${error.message}`);
        }
    }
    /**
     * Save bounce record to disk
     * @param bounce Bounce record to save
     */
    async saveBounceRecord(bounce) {
        try {
            const bounceData = JSON.stringify(bounce, null, 2);
            if (this.storageManager) {
                // Use storage manager
                await this.storageManager.set(`/email/bounces/records/${bounce.id}.json`, bounceData);
            }
            else {
                // Fall back to filesystem
                const bouncePath = plugins.path.join(paths.dataDir, 'emails', 'bounces', `${bounce.id}.json`);
                // Ensure directory exists
                const bounceDir = plugins.path.join(paths.dataDir, 'emails', 'bounces');
                await plugins.smartfs.directory(bounceDir).recursive().create();
                await plugins.smartfs.file(bouncePath).write(bounceData);
            }
        }
        catch (error) {
            logger.log('error', `Failed to save bounce record: ${error.message}`);
        }
    }
    /**
     * Update bounce cache with new bounce information
     * @param bounce Bounce record to update cache with
     */
    updateBounceCache(bounce) {
        const email = bounce.recipient.toLowerCase();
        const existing = this.bounceCache.get(email);
        if (existing) {
            // Update existing cache entry
            existing.lastBounce = bounce.timestamp;
            existing.count++;
            existing.type = bounce.bounceType;
            existing.category = bounce.bounceCategory;
        }
        else {
            // Create new cache entry
            this.bounceCache.set(email, {
                lastBounce: bounce.timestamp,
                count: 1,
                type: bounce.bounceType,
                category: bounce.bounceCategory
            });
        }
    }
    /**
     * Check bounce history for an email address
     * @param email Email address to check
     * @returns Bounce information or null if no bounces
     */
    getBounceInfo(email) {
        return this.bounceCache.get(email.toLowerCase()) || null;
    }
    /**
     * Analyze SMTP response and diagnostic codes to determine bounce type
     * @param smtpResponse SMTP response string
     * @param diagnosticCode Diagnostic code from bounce
     * @param statusCode Status code from bounce
     * @returns Detected bounce type and category
     */
    detectBounceType(smtpResponse, diagnosticCode, statusCode) {
        // Combine all text for comprehensive pattern matching
        const fullText = `${smtpResponse} ${diagnosticCode} ${statusCode}`.toLowerCase();
        // Check for auto-responses first
        if (this.matchesPattern(fullText, BounceType.AUTO_RESPONSE) ||
            this.matchesPattern(fullText, BounceType.CHALLENGE_RESPONSE)) {
            return {
                type: BounceType.AUTO_RESPONSE,
                category: BounceCategory.AUTO_RESPONSE
            };
        }
        // Check for hard bounces
        for (const bounceType of [
            BounceType.INVALID_RECIPIENT,
            BounceType.DOMAIN_NOT_FOUND,
            BounceType.MAILBOX_FULL,
            BounceType.MAILBOX_INACTIVE,
            BounceType.BLOCKED,
            BounceType.SPAM_RELATED,
            BounceType.POLICY_RELATED
        ]) {
            if (this.matchesPattern(fullText, bounceType)) {
                return {
                    type: bounceType,
                    category: BounceCategory.HARD
                };
            }
        }
        // Check for soft bounces
        for (const bounceType of [
            BounceType.SERVER_UNAVAILABLE,
            BounceType.TEMPORARY_FAILURE,
            BounceType.QUOTA_EXCEEDED,
            BounceType.NETWORK_ERROR,
            BounceType.TIMEOUT
        ]) {
            if (this.matchesPattern(fullText, bounceType)) {
                return {
                    type: bounceType,
                    category: BounceCategory.SOFT
                };
            }
        }
        // Handle DSN (Delivery Status Notification) status codes
        if (statusCode) {
            // Format: class.subject.detail
            const parts = statusCode.split('.');
            if (parts.length >= 2) {
                const statusClass = parts[0];
                const statusSubject = parts[1];
                // 5.X.X is permanent failure (hard bounce)
                if (statusClass === '5') {
                    // Try to determine specific type based on subject
                    if (statusSubject === '1') {
                        return { type: BounceType.INVALID_RECIPIENT, category: BounceCategory.HARD };
                    }
                    else if (statusSubject === '2') {
                        return { type: BounceType.MAILBOX_FULL, category: BounceCategory.HARD };
                    }
                    else if (statusSubject === '7') {
                        return { type: BounceType.BLOCKED, category: BounceCategory.HARD };
                    }
                    else {
                        return { type: BounceType.UNKNOWN, category: BounceCategory.HARD };
                    }
                }
                // 4.X.X is temporary failure (soft bounce)
                if (statusClass === '4') {
                    // Try to determine specific type based on subject
                    if (statusSubject === '2') {
                        return { type: BounceType.QUOTA_EXCEEDED, category: BounceCategory.SOFT };
                    }
                    else if (statusSubject === '3') {
                        return { type: BounceType.SERVER_UNAVAILABLE, category: BounceCategory.SOFT };
                    }
                    else if (statusSubject === '4') {
                        return { type: BounceType.NETWORK_ERROR, category: BounceCategory.SOFT };
                    }
                    else {
                        return { type: BounceType.TEMPORARY_FAILURE, category: BounceCategory.SOFT };
                    }
                }
            }
        }
        // Default to unknown
        return {
            type: BounceType.UNKNOWN,
            category: BounceCategory.UNKNOWN
        };
    }
    /**
     * Check if text matches any pattern for a bounce type
     * @param text Text to check against patterns
     * @param bounceType Bounce type to get patterns for
     * @returns Whether the text matches any pattern
     */
    matchesPattern(text, bounceType) {
        const patterns = BOUNCE_PATTERNS[bounceType];
        if (!patterns) {
            return false;
        }
        for (const pattern of patterns) {
            if (pattern.test(text)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Get all known hard bounced addresses
     * @returns Array of hard bounced email addresses
     */
    getHardBouncedAddresses() {
        const hardBounced = [];
        for (const [email, info] of this.bounceCache.entries()) {
            if (info.category === BounceCategory.HARD) {
                hardBounced.push(email);
            }
        }
        return hardBounced;
    }
    /**
     * Get suppression list
     * @returns Array of suppressed email addresses
     */
    getSuppressionList() {
        return Array.from(this.suppressionList.keys());
    }
    /**
     * Clear old bounce records (for maintenance)
     * @param olderThan Timestamp to remove records older than
     * @returns Number of records removed
     */
    clearOldBounceRecords(olderThan) {
        let removed = 0;
        this.bounceStore = this.bounceStore.filter(bounce => {
            if (bounce.timestamp < olderThan) {
                removed++;
                return false;
            }
            return true;
        });
        return removed;
    }
}
//# sourceMappingURL=data:application/json;base64, 