/**
 * Security Event Logger
 * Centralized NDJSON audit log for all security events.
 * Writes to logs/audit.log with auto-severity assignment.
 */

import fs from 'fs';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';

// ── Event Types ──

export enum SecurityEventType {
    // Malware events
    MALWARE_DETECTED = 'MALWARE_DETECTED',
    MALWARE_SCAN_CLEAN = 'MALWARE_SCAN_CLEAN',
    MALWARE_SCAN_ERROR = 'MALWARE_SCAN_ERROR',
    MALWARE_SCAN_SKIPPED = 'MALWARE_SCAN_SKIPPED',

    // File validation events
    FILE_VALIDATION_FAILED = 'FILE_VALIDATION_FAILED',
    FILE_EXTENSION_BLOCKED = 'FILE_EXTENSION_BLOCKED',
    FILE_SIZE_EXCEEDED = 'FILE_SIZE_EXCEEDED',
    FILENAME_SANITIZED = 'FILENAME_SANITIZED',

    // Content security events
    CONTENT_XSS_DETECTED = 'CONTENT_XSS_DETECTED',
    CONTENT_XSS_CLEAN = 'CONTENT_XSS_CLEAN',

    // Admin events
    CLAMAV_TOGGLE_CHANGED = 'CLAMAV_TOGGLE_CHANGED',
    AUDIT_LOG_ACCESSED = 'AUDIT_LOG_ACCESSED',

    // Upload events
    FILE_UPLOAD_SUCCESS = 'FILE_UPLOAD_SUCCESS',
    FILE_UPLOAD_BLOCKED = 'FILE_UPLOAD_BLOCKED',
}

export enum SecuritySeverity {
    CRITICAL = 'CRITICAL',
    HIGH = 'HIGH',
    MEDIUM = 'MEDIUM',
    LOW = 'LOW',
    INFO = 'INFO',
}

export enum SecurityCategory {
    MALWARE = 'MALWARE',
    FILE_VALIDATION = 'FILE_VALIDATION',
    FILE_CONTENT_SECURITY = 'FILE_CONTENT_SECURITY',
    ADMIN = 'ADMIN',
    UPLOAD = 'UPLOAD',
}

// ── Severity Mapping ──

const EVENT_SEVERITY: Record<SecurityEventType, SecuritySeverity> = {
    [SecurityEventType.MALWARE_DETECTED]: SecuritySeverity.CRITICAL,
    [SecurityEventType.MALWARE_SCAN_CLEAN]: SecuritySeverity.INFO,
    [SecurityEventType.MALWARE_SCAN_ERROR]: SecuritySeverity.HIGH,
    [SecurityEventType.MALWARE_SCAN_SKIPPED]: SecuritySeverity.MEDIUM,
    [SecurityEventType.FILE_VALIDATION_FAILED]: SecuritySeverity.MEDIUM,
    [SecurityEventType.FILE_EXTENSION_BLOCKED]: SecuritySeverity.HIGH,
    [SecurityEventType.FILE_SIZE_EXCEEDED]: SecuritySeverity.MEDIUM,
    [SecurityEventType.FILENAME_SANITIZED]: SecuritySeverity.LOW,
    [SecurityEventType.CONTENT_XSS_DETECTED]: SecuritySeverity.CRITICAL,
    [SecurityEventType.CONTENT_XSS_CLEAN]: SecuritySeverity.INFO,
    [SecurityEventType.CLAMAV_TOGGLE_CHANGED]: SecuritySeverity.HIGH,
    [SecurityEventType.AUDIT_LOG_ACCESSED]: SecuritySeverity.LOW,
    [SecurityEventType.FILE_UPLOAD_SUCCESS]: SecuritySeverity.INFO,
    [SecurityEventType.FILE_UPLOAD_BLOCKED]: SecuritySeverity.HIGH,
};

const EVENT_CATEGORY: Record<SecurityEventType, SecurityCategory> = {
    [SecurityEventType.MALWARE_DETECTED]: SecurityCategory.MALWARE,
    [SecurityEventType.MALWARE_SCAN_CLEAN]: SecurityCategory.MALWARE,
    [SecurityEventType.MALWARE_SCAN_ERROR]: SecurityCategory.MALWARE,
    [SecurityEventType.MALWARE_SCAN_SKIPPED]: SecurityCategory.MALWARE,
    [SecurityEventType.FILE_VALIDATION_FAILED]: SecurityCategory.FILE_VALIDATION,
    [SecurityEventType.FILE_EXTENSION_BLOCKED]: SecurityCategory.FILE_VALIDATION,
    [SecurityEventType.FILE_SIZE_EXCEEDED]: SecurityCategory.FILE_VALIDATION,
    [SecurityEventType.FILENAME_SANITIZED]: SecurityCategory.FILE_VALIDATION,
    [SecurityEventType.CONTENT_XSS_DETECTED]: SecurityCategory.FILE_CONTENT_SECURITY,
    [SecurityEventType.CONTENT_XSS_CLEAN]: SecurityCategory.FILE_CONTENT_SECURITY,
    [SecurityEventType.CLAMAV_TOGGLE_CHANGED]: SecurityCategory.ADMIN,
    [SecurityEventType.AUDIT_LOG_ACCESSED]: SecurityCategory.ADMIN,
    [SecurityEventType.FILE_UPLOAD_SUCCESS]: SecurityCategory.UPLOAD,
    [SecurityEventType.FILE_UPLOAD_BLOCKED]: SecurityCategory.UPLOAD,
};

// ── Configuration ──

const LOGS_DIR = path.resolve(process.cwd(), 'logs');
const AUDIT_LOG_FILE = path.join(LOGS_DIR, 'audit.log');

// ── Core Functions ──

/**
 * Log a security event in NDJSON format
 */
export function logSecurityEvent(
    eventType: SecurityEventType,
    data: Record<string, any> = {},
    userId?: string
): string {
    const eventId = uuidv4();

    const entry = {
        timestamp: new Date().toISOString(),
        eventId,
        eventType,
        severity: EVENT_SEVERITY[eventType] || SecuritySeverity.INFO,
        category: EVENT_CATEGORY[eventType] || SecurityCategory.UPLOAD,
        userId: userId || 'system',
        data,
    };

    try {
        // Ensure logs directory exists
        if (!fs.existsSync(LOGS_DIR)) {
            fs.mkdirSync(LOGS_DIR, { recursive: true });
        }

        // Append as NDJSON (one JSON object per line)
        fs.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + '\n');
    } catch (error) {
        console.error('[SecurityLogger] Failed to write audit log:', error);
    }

    return eventId;
}

/**
 * Read and parse audit log entries
 */
export function readAuditLogs(options: {
    eventType?: string;
    severity?: string;
    category?: string;
    startDate?: string;
    endDate?: string;
    limit?: number;
    offset?: number;
} = {}): { entries: any[]; total: number } {
    try {
        if (!fs.existsSync(AUDIT_LOG_FILE)) {
            return { entries: [], total: 0 };
        }

        const content = fs.readFileSync(AUDIT_LOG_FILE, 'utf-8');
        let entries = content
            .split('\n')
            .filter(line => line.trim())
            .map(line => {
                try {
                    return JSON.parse(line);
                } catch {
                    return null;
                }
            })
            .filter(Boolean)
            .reverse(); // Most recent first

        // Apply filters
        if (options.eventType) {
            entries = entries.filter(e => e.eventType === options.eventType);
        }
        if (options.severity) {
            entries = entries.filter(e => e.severity === options.severity);
        }
        if (options.category) {
            entries = entries.filter(e => e.category === options.category);
        }
        if (options.startDate) {
            const start = new Date(options.startDate);
            entries = entries.filter(e => new Date(e.timestamp) >= start);
        }
        if (options.endDate) {
            const end = new Date(options.endDate);
            entries = entries.filter(e => new Date(e.timestamp) <= end);
        }

        const total = entries.length;
        const offset = options.offset || 0;
        const limit = options.limit || 50;

        return {
            entries: entries.slice(offset, offset + limit),
            total,
        };
    } catch (error) {
        console.error('[SecurityLogger] Failed to read audit logs:', error);
        return { entries: [], total: 0 };
    }
}

/**
 * Get audit log statistics
 */
export function getAuditStats(): Record<string, any> {
    try {
        if (!fs.existsSync(AUDIT_LOG_FILE)) {
            return { totalEvents: 0, byType: {}, bySeverity: {}, byCategory: {} };
        }

        const content = fs.readFileSync(AUDIT_LOG_FILE, 'utf-8');
        const entries = content
            .split('\n')
            .filter(line => line.trim())
            .map(line => {
                try { return JSON.parse(line); } catch { return null; }
            })
            .filter(Boolean);

        const byType: Record<string, number> = {};
        const bySeverity: Record<string, number> = {};
        const byCategory: Record<string, number> = {};

        for (const entry of entries) {
            byType[entry.eventType] = (byType[entry.eventType] || 0) + 1;
            bySeverity[entry.severity] = (bySeverity[entry.severity] || 0) + 1;
            byCategory[entry.category] = (byCategory[entry.category] || 0) + 1;
        }

        return {
            totalEvents: entries.length,
            byType,
            bySeverity,
            byCategory,
            oldestEvent: entries[0]?.timestamp,
            newestEvent: entries[entries.length - 1]?.timestamp,
        };
    } catch (error) {
        console.error('[SecurityLogger] Failed to compute audit stats:', error);
        return { totalEvents: 0, byType: {}, bySeverity: {}, byCategory: {} };
    }
}

/**
 * Export audit logs as CSV
 */
export function exportAuditLogsCSV(options: {
    eventType?: string;
    severity?: string;
    startDate?: string;
    endDate?: string;
} = {}): string {
    const { entries } = readAuditLogs({ ...options, limit: 10000 });

    const headers = ['timestamp', 'eventId', 'eventType', 'severity', 'category', 'userId', 'data'];
    const rows = entries.map(entry => [
        entry.timestamp,
        entry.eventId,
        entry.eventType,
        entry.severity,
        entry.category,
        entry.userId,
        JSON.stringify(entry.data || {}),
    ].join(','));

    return [headers.join(','), ...rows].join('\n');
}
