import { getAuthenticatedClient } from '../../scripts/utils/directus-client';
import { EventEmitter } from 'events';

// Import Directus SDK functions
const directusSdk = require('@directus/sdk');
const createItem = directusSdk.createItem as any;
const readItems = directusSdk.readItems as any;
const aggregate = directusSdk.aggregate as any;

/**
 * Audit Log Entry Interface
 */
export interface AuditLogEntry {
  id?: string;
  timestamp: string;
  entity_type: 'task' | 'workflow' | 'webhook' | 'user' | 'system';
  entity_id: string;
  action: string;
  actor_id?: string;
  actor_type: 'user' | 'system' | 'api' | 'automation';
  details?: Record<string, any>;
  changes?: {
    field: string;
    old_value: any;
    new_value: any;
  }[];
  metadata?: {
    ip_address?: string;
    user_agent?: string;
    session_id?: string;
    api_key_id?: string;
    request_id?: string;
  };
  severity: 'info' | 'warning' | 'error' | 'critical';
  tags?: string[];
}

/**
 * Audit Query Options
 */
export interface AuditQueryOptions {
  entityType?: string;
  entityId?: string;
  actorId?: string;
  action?: string;
  severity?: string;
  startDate?: Date;
  endDate?: Date;
  tags?: string[];
  limit?: number;
  offset?: number;
  sort?: string;
}

/**
 * Audit Statistics
 */
export interface AuditStatistics {
  totalLogs: number;
  byEntityType: Record<string, number>;
  byAction: Record<string, number>;
  bySeverity: Record<string, number>;
  byActor: Record<string, number>;
  timeRange: {
    start: string;
    end: string;
  };
}

/**
 * Audit Logger Service
 * Comprehensive audit logging for all system activities
 */
export class AuditLogger extends EventEmitter {
  private client: any;
  private buffer: AuditLogEntry[] = [];
  private bufferSize: number = 100;
  private flushInterval: NodeJS.Timeout | null = null;
  private flushIntervalMs: number = 5000;
  private enabled: boolean = true;

  constructor() {
    super();
    this.initializeClient();
    this.startBufferFlush();
  }

  private async initializeClient() {
    this.client = await getAuthenticatedClient();
  }

  /**
   * Start buffer flush interval
   */
  private startBufferFlush(): void {
    this.flushInterval = setInterval(async () => {
      if (this.buffer.length > 0) {
        await this.flush();
      }
    }, this.flushIntervalMs);
  }

  /**
   * Stop buffer flush interval
   */
  private stopBufferFlush(): void {
    if (this.flushInterval) {
      clearInterval(this.flushInterval);
      this.flushInterval = null;
    }
  }

  /**
   * Log an audit entry
   */
  async log(entry: Omit<AuditLogEntry, 'timestamp'>): Promise<void> {
    if (!this.enabled) return;

    const logEntry: AuditLogEntry = {
      ...entry,
      timestamp: new Date().toISOString()
    };

    // Add to buffer
    this.buffer.push(logEntry);

    // Emit event for real-time monitoring
    this.emit('log', logEntry);

    // Check if buffer should be flushed
    if (this.buffer.length >= this.bufferSize) {
      await this.flush();
    }

    // Log critical events immediately
    if (entry.severity === 'critical') {
      await this.flush();
    }
  }

  /**
   * Log task action
   */
  async logTaskAction(
    taskId: string,
    action: string,
    actorId?: string,
    details?: Record<string, any>,
    changes?: any[]
  ): Promise<void> {
    await this.log({
      entity_type: 'task',
      entity_id: taskId,
      action,
      actor_id: actorId,
      actor_type: actorId ? 'user' : 'system',
      details,
      changes,
      severity: 'info'
    });
  }

  /**
   * Log workflow transition
   */
  async logWorkflowTransition(
    taskId: string,
    fromStatus: string,
    toStatus: string,
    triggeredBy: string,
    metadata?: Record<string, any>
  ): Promise<void> {
    await this.log({
      entity_type: 'workflow',
      entity_id: taskId,
      action: 'status_transition',
      actor_type: 'system',
      details: {
        from_status: fromStatus,
        to_status: toStatus,
        triggered_by: triggeredBy,
        ...metadata
      },
      severity: 'info',
      tags: ['workflow', 'transition']
    });
  }

  /**
   * Log webhook activity
   */
  async logWebhookActivity(
    webhookId: string,
    action: string,
    success: boolean,
    details?: Record<string, any>
  ): Promise<void> {
    await this.log({
      entity_type: 'webhook',
      entity_id: webhookId,
      action,
      actor_type: 'system',
      details: {
        success,
        ...details
      },
      severity: success ? 'info' : 'warning',
      tags: ['webhook']
    });
  }

  /**
   * Log error
   */
  async logError(
    entityType: AuditLogEntry['entity_type'],
    entityId: string,
    error: Error,
    context?: Record<string, any>
  ): Promise<void> {
    await this.log({
      entity_type: entityType,
      entity_id: entityId,
      action: 'error',
      actor_type: 'system',
      details: {
        error_message: error.message,
        error_stack: error.stack,
        ...context
      },
      severity: 'error',
      tags: ['error']
    });
  }

  /**
   * Log security event
   */
  async logSecurityEvent(
    action: string,
    actorId: string,
    success: boolean,
    details?: Record<string, any>
  ): Promise<void> {
    await this.log({
      entity_type: 'system',
      entity_id: 'security',
      action,
      actor_id: actorId,
      actor_type: 'user',
      details: {
        success,
        ...details
      },
      severity: success ? 'info' : 'warning',
      tags: ['security']
    });
  }

  /**
   * Flush buffer to database
   */
  async flush(): Promise<void> {
    if (this.buffer.length === 0) return;
    if (!this.client) await this.initializeClient();

    const entries = [...this.buffer];
    this.buffer = [];

    try {
      // Batch insert audit logs
      await this.client.request(
        createItem('audit_logs', entries)
      );

      console.log(`Flushed ${entries.length} audit log entries`);
    } catch (error) {
      console.error('Failed to flush audit logs:', error);
      
      // Re-add entries to buffer for retry
      this.buffer.unshift(...entries);
      
      // Emit error event
      this.emit('flushError', error);
    }
  }

  /**
   * Query audit logs
   */
  async query(options: AuditQueryOptions = {}): Promise<AuditLogEntry[]> {
    if (!this.client) await this.initializeClient();

    // Build filter
    const filter: any = {};

    if (options.entityType) {
      filter.entity_type = { _eq: options.entityType };
    }
    if (options.entityId) {
      filter.entity_id = { _eq: options.entityId };
    }
    if (options.actorId) {
      filter.actor_id = { _eq: options.actorId };
    }
    if (options.action) {
      filter.action = { _eq: options.action };
    }
    if (options.severity) {
      filter.severity = { _eq: options.severity };
    }
    if (options.startDate) {
      filter.timestamp = { 
        ...filter.timestamp,
        _gte: options.startDate.toISOString() 
      };
    }
    if (options.endDate) {
      filter.timestamp = { 
        ...filter.timestamp,
        _lte: options.endDate.toISOString() 
      };
    }
    if (options.tags && options.tags.length > 0) {
      filter.tags = { _contains: options.tags };
    }

    try {
      const logs = await this.client.request(
        readItems('audit_logs', {
          filter,
          limit: options.limit || 100,
          offset: options.offset || 0,
          sort: options.sort || ['-timestamp']
        })
      );

      return logs;
    } catch (error: any) {
      throw new Error(`Failed to query audit logs: ${error.message}`);
    }
  }

  /**
   * Get audit statistics
   */
  async getStatistics(
    startDate?: Date,
    endDate?: Date
  ): Promise<AuditStatistics> {
    if (!this.client) await this.initializeClient();

    const filter: any = {};
    if (startDate) {
      filter.timestamp = { 
        ...filter.timestamp,
        _gte: startDate.toISOString() 
      };
    }
    if (endDate) {
      filter.timestamp = { 
        ...filter.timestamp,
        _lte: endDate.toISOString() 
      };
    }

    try {
      // Get aggregated statistics
      const stats = await this.client.request(
        aggregate('audit_logs', {
          aggregate: {
            count: '*',
            countDistinct: ['entity_type', 'action', 'severity', 'actor_id']
          },
          groupBy: ['entity_type', 'action', 'severity'],
          filter
        })
      );

      // Get time range
      const timeRange = await this.client.request(
        aggregate('audit_logs', {
          aggregate: {
            min: ['timestamp'],
            max: ['timestamp']
          },
          filter
        })
      );

      // Process and format statistics
      const statistics: AuditStatistics = {
        totalLogs: stats[0]?.count || 0,
        byEntityType: {},
        byAction: {},
        bySeverity: {},
        byActor: {},
        timeRange: {
          start: timeRange[0]?.timestamp_min || '',
          end: timeRange[0]?.timestamp_max || ''
        }
      };

      // Group statistics
      stats.forEach((stat: any) => {
        if (stat.entity_type) {
          statistics.byEntityType[stat.entity_type] = 
            (statistics.byEntityType[stat.entity_type] || 0) + stat.count;
        }
        if (stat.action) {
          statistics.byAction[stat.action] = 
            (statistics.byAction[stat.action] || 0) + stat.count;
        }
        if (stat.severity) {
          statistics.bySeverity[stat.severity] = 
            (statistics.bySeverity[stat.severity] || 0) + stat.count;
        }
      });

      return statistics;
    } catch (error: any) {
      throw new Error(`Failed to get audit statistics: ${error.message}`);
    }
  }

  /**
   * Get audit timeline
   */
  async getTimeline(
    entityId: string,
    entityType?: string
  ): Promise<AuditLogEntry[]> {
    const options: AuditQueryOptions = {
      entityId,
      entityType,
      sort: 'timestamp'
    };

    return await this.query(options);
  }

  /**
   * Archive old audit logs
   */
  async archiveOldLogs(daysToKeep: number = 90): Promise<number> {
    if (!this.client) await this.initializeClient();

    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

    try {
      // Get logs to archive
      const oldLogs = await this.client.request(
        readItems('audit_logs', {
          filter: {
            timestamp: { _lt: cutoffDate.toISOString() }
          }
        })
      );

      if (oldLogs.length === 0) {
        console.log('No audit logs to archive');
        return 0;
      }

      // Archive to separate collection or external storage
      await this.client.request(
        createItem('audit_logs_archive', oldLogs)
      );

      // Delete archived logs
      const logIds = oldLogs.map((log: any) => log.id);
      for (const id of logIds) {
        await this.client.request(
          deleteItem('audit_logs', id)
        );
      }

      console.log(`Archived ${oldLogs.length} audit logs`);
      return oldLogs.length;
    } catch (error: any) {
      throw new Error(`Failed to archive audit logs: ${error.message}`);
    }
  }

  /**
   * Export audit logs
   */
  async exportLogs(
    options: AuditQueryOptions,
    format: 'json' | 'csv' = 'json'
  ): Promise<string> {
    const logs = await this.query(options);

    if (format === 'json') {
      return JSON.stringify(logs, null, 2);
    }

    // CSV format
    if (logs.length === 0) return '';

    const headers = Object.keys(logs[0]).join(',');
    const rows = logs.map(log => {
      return Object.values(log).map(value => {
        if (typeof value === 'object') {
          return JSON.stringify(value);
        }
        return value;
      }).join(',');
    });

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

  /**
   * Enable or disable audit logging
   */
  setEnabled(enabled: boolean): void {
    this.enabled = enabled;
    console.log(`Audit logging ${enabled ? 'enabled' : 'disabled'}`);
  }

  /**
   * Clean up service
   */
  async cleanup(): Promise<void> {
    // Flush remaining logs
    await this.flush();
    
    // Stop flush interval
    this.stopBufferFlush();
    
    // Clear buffer
    this.buffer = [];
    
    // Remove all listeners
    this.removeAllListeners();
  }
}

// Export singleton instance
export const auditLogger = new AuditLogger();