import { EventEmitter } from 'events';

export interface EscalationRule {
  id: string;
  alertId: string;
  name: string;
  enabled: boolean;
  escalationLevels: EscalationLevel[];
  createdAt: Date;
  updatedAt: Date;
}

export interface EscalationLevel {
  level: number;
  delayMinutes: number;
  recipients: string[];
  notificationChannels: ('email' | 'sms' | 'push' | 'slack' | 'discord' | 'teams')[];
  escalationMessage: string;
  priority: 'low' | 'medium' | 'high' | 'critical';
}

export interface AlertEscalation {
  id: string;
  alertId: string;
  ruleId: string;
  currentLevel: number;
  status: 'pending' | 'escalated' | 'resolved' | 'acknowledged';
  createdAt: Date;
  acknowledgedAt?: Date;
  acknowledgedBy?: string;
  escalationHistory: EscalationEvent[];
}

export interface EscalationEvent {
  level: number;
  timestamp: Date;
  recipients: string[];
  channels: string[];
  status: 'sent' | 'failed' | 'acknowledged';
  message: string;
}

class AlertEscalationSystem extends EventEmitter {
  private escalations = new Map<string, AlertEscalation>();
  private rules = new Map<string, EscalationRule>();
  private escalationTimers = new Map<string, NodeJS.Timeout>();

  /**
   * Create escalation rule for an alert
   */
  createEscalationRule(rule: Omit<EscalationRule, 'id' | 'createdAt' | 'updatedAt'>): EscalationRule {
    const id = `rule_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const fullRule: EscalationRule = {
      ...rule,
      id,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
    this.rules.set(id, fullRule);
    this.emit('rule:created', fullRule);
    return fullRule;
  }

  /**
   * Update escalation rule
   */
  updateEscalationRule(ruleId: string, updates: Partial<EscalationRule>): EscalationRule | null {
    const rule = this.rules.get(ruleId);
    if (!rule) return null;

    const updated: EscalationRule = {
      ...rule,
      ...updates,
      updatedAt: new Date(),
    };
    this.rules.set(ruleId, updated);
    this.emit('rule:updated', updated);
    return updated;
  }

  /**
   * Delete escalation rule
   */
  deleteEscalationRule(ruleId: string): boolean {
    const deleted = this.rules.delete(ruleId);
    if (deleted) {
      this.emit('rule:deleted', ruleId);
    }
    return deleted;
  }

  /**
   * Get escalation rule
   */
  getEscalationRule(ruleId: string): EscalationRule | null {
    return this.rules.get(ruleId) || null;
  }

  /**
   * List all escalation rules
   */
  listEscalationRules(alertId?: string): EscalationRule[] {
    const rules = Array.from(this.rules.values());
    return alertId ? rules.filter(r => r.alertId === alertId) : rules;
  }

  /**
   * Create escalation for alert
   */
  createEscalation(alertId: string, ruleId: string): AlertEscalation | null {
    const rule = this.rules.get(ruleId);
    if (!rule || !rule.enabled) return null;

    const id = `esc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const escalation: AlertEscalation = {
      id,
      alertId,
      ruleId,
      currentLevel: 0,
      status: 'pending',
      createdAt: new Date(),
      escalationHistory: [],
    };

    this.escalations.set(id, escalation);
    this.scheduleNextEscalation(id);
    this.emit('escalation:created', escalation);
    return escalation;
  }

  /**
   * Schedule next escalation level
   */
  private scheduleNextEscalation(escalationId: string): void {
    const escalation = this.escalations.get(escalationId);
    if (!escalation) return;

    const rule = this.rules.get(escalation.ruleId);
    if (!rule) return;

    const nextLevel = escalation.currentLevel + 1;
    if (nextLevel >= rule.escalationLevels.length) {
      escalation.status = 'resolved';
      this.emit('escalation:resolved', escalation);
      return;
    }

    const level = rule.escalationLevels[nextLevel];
    const delayMs = level.delayMinutes * 60 * 1000;

    // Clear existing timer
    const existingTimer = this.escalationTimers.get(escalationId);
    if (existingTimer) clearTimeout(existingTimer);

    // Schedule new escalation
    const timer = setTimeout(() => {
      this.escalateAlert(escalationId);
    }, delayMs);

    this.escalationTimers.set(escalationId, timer);
  }

  /**
   * Execute escalation to next level
   */
  private escalateAlert(escalationId: string): void {
    const escalation = this.escalations.get(escalationId);
    if (!escalation) return;

    const rule = this.rules.get(escalation.ruleId);
    if (!rule) return;

    const level = rule.escalationLevels[escalation.currentLevel + 1];
    if (!level) return;

    escalation.currentLevel += 1;
    escalation.status = 'escalated';

    const event: EscalationEvent = {
      level: escalation.currentLevel,
      timestamp: new Date(),
      recipients: level.recipients,
      channels: level.notificationChannels,
      status: 'sent',
      message: level.escalationMessage,
    };

    escalation.escalationHistory.push(event);
    this.emit('escalation:level', { escalationId, level: escalation.currentLevel, event });

    // Schedule next level
    this.scheduleNextEscalation(escalationId);
  }

  /**
   * Acknowledge escalation
   */
  acknowledgeEscalation(escalationId: string, acknowledgedBy: string): AlertEscalation | null {
    const escalation = this.escalations.get(escalationId);
    if (!escalation) return null;

    escalation.status = 'acknowledged';
    escalation.acknowledgedAt = new Date();
    escalation.acknowledgedBy = acknowledgedBy;

    // Clear pending escalation timer
    const timer = this.escalationTimers.get(escalationId);
    if (timer) {
      clearTimeout(timer);
      this.escalationTimers.delete(escalationId);
    }

    this.emit('escalation:acknowledged', escalation);
    return escalation;
  }

  /**
   * Resolve escalation
   */
  resolveEscalation(escalationId: string): AlertEscalation | null {
    const escalation = this.escalations.get(escalationId);
    if (!escalation) return null;

    escalation.status = 'resolved';

    // Clear pending escalation timer
    const timer = this.escalationTimers.get(escalationId);
    if (timer) {
      clearTimeout(timer);
      this.escalationTimers.delete(escalationId);
    }

    this.emit('escalation:resolved', escalation);
    return escalation;
  }

  /**
   * Get escalation
   */
  getEscalation(escalationId: string): AlertEscalation | null {
    return this.escalations.get(escalationId) || null;
  }

  /**
   * List escalations by alert
   */
  listEscalationsByAlert(alertId: string): AlertEscalation[] {
    return Array.from(this.escalations.values()).filter(e => e.alertId === alertId);
  }

  /**
   * List active escalations
   */
  listActiveEscalations(): AlertEscalation[] {
    return Array.from(this.escalations.values()).filter(e => e.status === 'pending' || e.status === 'escalated');
  }

  /**
   * Get escalation statistics
   */
  getEscalationStats(): {
    total: number;
    active: number;
    acknowledged: number;
    resolved: number;
    avgTimeToAcknowledge: number;
    avgEscalationLevels: number;
  } {
    const escalations = Array.from(this.escalations.values());
    const acknowledged = escalations.filter(e => e.status === 'acknowledged');
    const resolved = escalations.filter(e => e.status === 'resolved');

    const timeToAcknowledge = acknowledged
      .filter(e => e.acknowledgedAt)
      .map(e => (e.acknowledgedAt!.getTime() - e.createdAt.getTime()) / 1000 / 60)
      .reduce((a, b) => a + b, 0) / (acknowledged.length || 1);

    const avgLevels = escalations
      .map(e => e.escalationHistory.length)
      .reduce((a, b) => a + b, 0) / (escalations.length || 1);

    return {
      total: escalations.length,
      active: escalations.filter(e => e.status === 'pending' || e.status === 'escalated').length,
      acknowledged: acknowledged.length,
      resolved: resolved.length,
      avgTimeToAcknowledge: timeToAcknowledge,
      avgEscalationLevels: avgLevels,
    };
  }
}

export const alertEscalationSystem = new AlertEscalationSystem();
