import { EventEmitter } from 'events';

export interface AuditEntry {
  id: string;
  alertId: string;
  action: 'created' | 'updated' | 'deleted' | 'tested' | 'triggered' | 'acknowledged' | 'resolved' | 'escalated';
  performedBy: string;
  timestamp: Date;
  changes?: {
    field: string;
    oldValue: any;
    newValue: any;
  }[];
  details: Record<string, any>;
  ipAddress?: string;
  userAgent?: string;
  status: 'success' | 'failed';
  errorMessage?: string;
}

export interface AuditFilter {
  alertId?: string;
  action?: string;
  performedBy?: string;
  startDate?: Date;
  endDate?: Date;
  status?: 'success' | 'failed';
}

class AlertAuditTrailSystem extends EventEmitter {
  private auditLog: AuditEntry[] = [];
  private maxEntries = 100000;

  /**
   * Record audit entry
   */
  recordEntry(entry: Omit<AuditEntry, 'id' | 'timestamp'>): AuditEntry {
    const fullEntry: AuditEntry = {
      ...entry,
      id: `audit_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      timestamp: new Date(),
    };

    this.auditLog.push(fullEntry);

    // Trim old entries if exceeding max
    if (this.auditLog.length > this.maxEntries) {
      this.auditLog = this.auditLog.slice(-this.maxEntries);
    }

    this.emit('entry:recorded', fullEntry);
    return fullEntry;
  }

  /**
   * Get audit entry by ID
   */
  getEntry(entryId: string): AuditEntry | null {
    return this.auditLog.find(e => e.id === entryId) || null;
  }

  /**
   * Get audit entries for alert
   */
  getAlertAuditTrail(alertId: string): AuditEntry[] {
    return this.auditLog.filter(e => e.alertId === alertId);
  }

  /**
   * Get audit entries for user
   */
  getUserAuditTrail(userId: string): AuditEntry[] {
    return this.auditLog.filter(e => e.performedBy === userId);
  }

  /**
   * Search audit log with filters
   */
  searchAuditLog(filters: AuditFilter): AuditEntry[] {
    return this.auditLog.filter(entry => {
      if (filters.alertId && entry.alertId !== filters.alertId) return false;
      if (filters.action && entry.action !== filters.action) return false;
      if (filters.performedBy && entry.performedBy !== filters.performedBy) return false;
      if (filters.status && entry.status !== filters.status) return false;
      if (filters.startDate && entry.timestamp < filters.startDate) return false;
      if (filters.endDate && entry.timestamp > filters.endDate) return false;
      return true;
    });
  }

  /**
   * Get audit statistics
   */
  getAuditStats(alertId?: string): {
    totalEntries: number;
    successCount: number;
    failureCount: number;
    actionBreakdown: Record<string, number>;
    recentEntries: AuditEntry[];
  } {
    const entries = alertId ? this.getAlertAuditTrail(alertId) : this.auditLog;

    const actionBreakdown: Record<string, number> = {};
    entries.forEach(e => {
      actionBreakdown[e.action] = (actionBreakdown[e.action] || 0) + 1;
    });

    return {
      totalEntries: entries.length,
      successCount: entries.filter(e => e.status === 'success').length,
      failureCount: entries.filter(e => e.status === 'failed').length,
      actionBreakdown,
      recentEntries: entries.slice(-10),
    };
  }

  /**
   * Export audit log as CSV
   */
  exportAsCSV(filters?: AuditFilter): string {
    const entries = filters ? this.searchAuditLog(filters) : this.auditLog;

    const headers = ['ID', 'Alert ID', 'Action', 'Performed By', 'Timestamp', 'Status', 'Details'];
    const rows = entries.map(e => [
      e.id,
      e.alertId,
      e.action,
      e.performedBy,
      e.timestamp.toISOString(),
      e.status,
      JSON.stringify(e.details),
    ]);

    const csv = [headers, ...rows].map(row => row.map(cell => `"${cell}"`).join(',')).join('\n');
    return csv;
  }

  /**
   * Export audit log as JSON
   */
  exportAsJSON(filters?: AuditFilter): string {
    const entries = filters ? this.searchAuditLog(filters) : this.auditLog;
    return JSON.stringify(entries, null, 2);
  }

  /**
   * Get audit trail for compliance
   */
  getComplianceReport(startDate: Date, endDate: Date): {
    period: { start: Date; end: Date };
    totalActions: number;
    actionsByType: Record<string, number>;
    failedActions: AuditEntry[];
    suspiciousActivity: AuditEntry[];
    userActivity: Record<string, number>;
  } {
    const entries = this.searchAuditLog({ startDate, endDate });

    const actionsByType: Record<string, number> = {};
    entries.forEach(e => {
      actionsByType[e.action] = (actionsByType[e.action] || 0) + 1;
    });

    const failedActions = entries.filter(e => e.status === 'failed');

    // Detect suspicious activity (multiple failed attempts, rapid changes, etc.)
    const suspiciousActivity = entries.filter(e => {
      if (e.status === 'failed') return true;
      if (e.action === 'deleted') return true;
      return false;
    });

    const userActivity: Record<string, number> = {};
    entries.forEach(e => {
      userActivity[e.performedBy] = (userActivity[e.performedBy] || 0) + 1;
    });

    return {
      period: { start: startDate, end: endDate },
      totalActions: entries.length,
      actionsByType,
      failedActions,
      suspiciousActivity,
      userActivity,
    };
  }

  /**
   * Clear old audit entries
   */
  clearOldEntries(beforeDate: Date): number {
    const initialLength = this.auditLog.length;
    this.auditLog = this.auditLog.filter(e => e.timestamp >= beforeDate);
    const deletedCount = initialLength - this.auditLog.length;
    this.emit('entries:cleared', { deletedCount, beforeDate });
    return deletedCount;
  }

  /**
   * Get audit log size
   */
  getLogSize(): {
    totalEntries: number;
    oldestEntry: Date | null;
    newestEntry: Date | null;
    averageEntriesPerDay: number;
  } {
    if (this.auditLog.length === 0) {
      return {
        totalEntries: 0,
        oldestEntry: null,
        newestEntry: null,
        averageEntriesPerDay: 0,
      };
    }

    const oldest = this.auditLog[0].timestamp;
    const newest = this.auditLog[this.auditLog.length - 1].timestamp;
    const daysDiff = (newest.getTime() - oldest.getTime()) / (1000 * 60 * 60 * 24);

    return {
      totalEntries: this.auditLog.length,
      oldestEntry: oldest,
      newestEntry: newest,
      averageEntriesPerDay: daysDiff > 0 ? this.auditLog.length / daysDiff : 0,
    };
  }

  /**
   * Rollback changes (for audit purposes, returns what was changed)
   */
  getRollbackInfo(entryId: string): {
    entry: AuditEntry | null;
    rollbackChanges: Array<{ field: string; from: any; to: any }>;
  } {
    const entry = this.getEntry(entryId);
    if (!entry || !entry.changes) {
      return { entry, rollbackChanges: [] };
    }

    const rollbackChanges = entry.changes.map(change => ({
      field: change.field,
      from: change.newValue,
      to: change.oldValue,
    }));

    return { entry, rollbackChanges };
  }
}

export const alertAuditTrailSystem = new AlertAuditTrailSystem();
