/**
 * SLA Monitor - Commercial-Grade Service Level Agreement Tracking
 *
 * REAL tracking infrastructure — the record* methods, thresholds, violations and generateReport()
 * are real and compute real values WHEN FED. But most slaStatus recorders (recordUptime,
 * recordSovereignModeLiveness, recordHeartbeatDetection, recordMeshSync, recordPQCHandshake, and
 * causal-integrity) currently have NO live caller, so their currentMetrics stay at init defaults;
 * only recordWANRestore + recordDeltaSyncStart are wired. Accordingly the client (F7, 2026-07-12)
 * renders getResilienceDashboard()'s slaStatus block as "2026 SLA Targets — not continuously
 * measured from live telemetry", NOT as measured compliance — do not read this block as live
 * numbers until the recorders are wired to real measurement sources.
 * getResilienceDashboard()'s clockDrift, syncLatency and pqcOverhead cpu/memory were
 * previously SYNTHETIC (Math.random) and rendered as real telemetry with no visible label; as of the
 * class-B fabrication sweep (2026-07-03) they are nulled at the source (empty / not-measured) and the
 * client renders an honest "not wired to real node telemetry" note. No Math.random remains in this module.
 *
 * Monitors and enforces 2026 SLA commitments for Tier-1 banks:
 * - 99.99% uptime in cloud-connected mode
 * - 100% transaction liveness in Sovereign Mode
 * - <500ms heartbeat failure detection
 * - <30s mesh synchronization RPO
 * - <1s Delta-Sync initiation RTO
 * - 500 TPS reconciliation throughput
 * - <200ms PQC handshake latency
 */

import { EventEmitter } from 'events';
import { logger } from './lib/logger';

const slaLogger = logger.child('sla');

export interface SLAMetrics {
  uptimePercentage: number;
  sovereignModeLiveness: number;
  heartbeatDetectionMs: number;
  meshSyncRPOSeconds: number;
  deltaSyncRTOMs: number;
  reconciliationTPS: number;
  pqcHandshakeMs: number;
  hlcDriftMs: number;
  causalIntegrityScore: number;
  doubleSpendAttempts: number;
  doubleSpendDetected: number;
}

export interface SLAViolation {
  id: string;
  timestamp: Date;
  metric: keyof SLAMetrics;
  threshold: number;
  actual: number;
  severity: 'WARNING' | 'CRITICAL' | 'BREACH';
  creditPercentage: number;
  description: string;
  resolved: boolean;
  resolvedAt?: Date;
}

export interface SLAThresholds {
  uptimePercentage: number;           // 99.99%
  sovereignModeLiveness: number;      // 100%
  heartbeatDetectionMs: number;       // 500ms
  meshSyncRPOSeconds: number;         // 30s
  deltaSyncRTOMs: number;             // 1000ms
  reconciliationTPS: number;          // 500 TPS
  pqcHandshakeMs: number;             // 200ms
  hlcDriftMs: number;                 // 100ms
  causalIntegrityScore: number;       // 99.99%
}

export interface SLAReport {
  reportId: string;
  periodStart: Date;
  periodEnd: Date;
  metrics: SLAMetrics;
  violations: SLAViolation[];
  totalCreditPercentage: number;
  status: 'COMPLIANT' | 'WARNING' | 'BREACH';
  merkleProof?: string;
}

/**
 * SLA Monitor - Tracks and enforces commercial SLA commitments
 */
export class SLAMonitor extends EventEmitter {
  private thresholds: SLAThresholds;
  private currentMetrics: SLAMetrics;
  private violations: SLAViolation[] = [];
  private measurementHistory: Array<{ timestamp: Date; metrics: SLAMetrics }> = [];
  private wanRestoreTimestamp: number | null = null;
  private deltaSyncStartTimestamp: number | null = null;
  private monitoringInterval: NodeJS.Timeout | null = null;

  constructor(customThresholds?: Partial<SLAThresholds>) {
    super();

    // Default commercial SLA thresholds for 2026
    this.thresholds = {
      uptimePercentage: 99.99,
      sovereignModeLiveness: 100,
      heartbeatDetectionMs: 500,
      meshSyncRPOSeconds: 30,
      deltaSyncRTOMs: 1000,
      reconciliationTPS: 500,
      pqcHandshakeMs: 200,
      hlcDriftMs: 100,
      causalIntegrityScore: 99.99,
      ...customThresholds
    };

    this.currentMetrics = {
      uptimePercentage: 100,
      sovereignModeLiveness: 100,
      heartbeatDetectionMs: 0,
      meshSyncRPOSeconds: 0,
      deltaSyncRTOMs: 0,
      reconciliationTPS: 0,
      pqcHandshakeMs: 0,
      hlcDriftMs: 0,
      causalIntegrityScore: 100,
      doubleSpendAttempts: 0,
      doubleSpendDetected: 0
    };

    slaLogger.info('Monitor initialized with 2026 commercial thresholds');
  }

  /**
   * Start continuous SLA monitoring
   */
  startMonitoring(intervalMs: number = 5000): void {
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
    }

    this.monitoringInterval = setInterval(() => {
      this.checkAllMetrics();
      this.recordMeasurement();
    }, intervalMs);

    slaLogger.info('Monitoring started', { metadata: { intervalMs } });
  }

  /**
   * Stop continuous monitoring
   */
  stopMonitoring(): void {
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
      this.monitoringInterval = null;
    }
    slaLogger.info('Monitoring stopped');
  }

  /**
   * Record WAN restoration event - starts RTO timer
   */
  recordWANRestore(): void {
    this.wanRestoreTimestamp = Date.now();
    slaLogger.info('WAN restore detected - RTO timer started');
    this.emit('wanRestored', { timestamp: this.wanRestoreTimestamp });
  }

  /**
   * Record Delta-Sync initiation - calculates RTO
   */
  recordDeltaSyncStart(): void {
    this.deltaSyncStartTimestamp = Date.now();
    
    if (this.wanRestoreTimestamp) {
      const rtoMs = this.deltaSyncStartTimestamp - this.wanRestoreTimestamp;
      this.currentMetrics.deltaSyncRTOMs = rtoMs;
      
      slaLogger.info('Delta-Sync initiated', { metadata: { rtoMs } });
      
      if (rtoMs > this.thresholds.deltaSyncRTOMs) {
        this.recordViolation('deltaSyncRTOMs', this.thresholds.deltaSyncRTOMs, rtoMs);
      }
      
      // Reset timer
      this.wanRestoreTimestamp = null;
    }
    
    this.emit('deltaSyncStarted', { 
      timestamp: this.deltaSyncStartTimestamp,
      rtoMs: this.currentMetrics.deltaSyncRTOMs 
    });
  }

  /**
   * Record heartbeat failure detection latency
   */
  recordHeartbeatDetection(detectionMs: number): void {
    this.currentMetrics.heartbeatDetectionMs = detectionMs;
    
    if (detectionMs > this.thresholds.heartbeatDetectionMs) {
      this.recordViolation('heartbeatDetectionMs', this.thresholds.heartbeatDetectionMs, detectionMs);
    }
    
    slaLogger.debug('Heartbeat detection', { metadata: { detectionMs } });
  }

  /**
   * Record PQC handshake latency
   */
  recordPQCHandshake(latencyMs: number): void {
    this.currentMetrics.pqcHandshakeMs = latencyMs;
    
    if (latencyMs > this.thresholds.pqcHandshakeMs) {
      this.recordViolation('pqcHandshakeMs', this.thresholds.pqcHandshakeMs, latencyMs);
    }
    
    slaLogger.debug('PQC handshake', { metadata: { latencyMs } });
  }

  /**
   * Record mesh sync latency (RPO)
   */
  recordMeshSync(rpoSeconds: number): void {
    this.currentMetrics.meshSyncRPOSeconds = rpoSeconds;
    
    if (rpoSeconds > this.thresholds.meshSyncRPOSeconds) {
      this.recordViolation('meshSyncRPOSeconds', this.thresholds.meshSyncRPOSeconds, rpoSeconds);
    }
    
    slaLogger.debug('Mesh sync RPO', { metadata: { rpoSeconds } });
  }

  /**
   * Record reconciliation throughput
   */
  recordReconciliationTPS(tps: number): void {
    this.currentMetrics.reconciliationTPS = tps;
    
    if (tps < this.thresholds.reconciliationTPS) {
      this.recordViolation('reconciliationTPS', this.thresholds.reconciliationTPS, tps);
    }
    
    slaLogger.debug('Reconciliation TPS', { metadata: { tps } });
  }

  /**
   * Record HLC clock drift between mesh nodes
   */
  recordHLCDrift(driftMs: number): void {
    this.currentMetrics.hlcDriftMs = driftMs;
    
    if (driftMs > this.thresholds.hlcDriftMs) {
      this.recordViolation('hlcDriftMs', this.thresholds.hlcDriftMs, driftMs);
    }
  }

  /**
   * Record double-spend attempt and detection
   */
  recordDoubleSpendAttempt(detected: boolean): void {
    this.currentMetrics.doubleSpendAttempts++;
    
    if (detected) {
      this.currentMetrics.doubleSpendDetected++;
      slaLogger.warn('Double-spend attempt DETECTED and blocked');
    } else {
      // Critical SLA breach - undetected double-spend
      this.recordViolation('causalIntegrityScore', 100, 0, 'BREACH');
      slaLogger.error('CRITICAL: Double-spend NOT detected - Integrity breach');
    }
    
    // Update causal integrity score
    if (this.currentMetrics.doubleSpendAttempts > 0) {
      this.currentMetrics.causalIntegrityScore = 
        (this.currentMetrics.doubleSpendDetected / this.currentMetrics.doubleSpendAttempts) * 100;
    }
  }

  /**
   * Record uptime measurement
   */
  recordUptime(uptimePercentage: number): void {
    this.currentMetrics.uptimePercentage = uptimePercentage;
    
    if (uptimePercentage < this.thresholds.uptimePercentage) {
      this.recordViolation('uptimePercentage', this.thresholds.uptimePercentage, uptimePercentage);
    }
  }

  /**
   * Record Sovereign Mode liveness
   */
  recordSovereignModeLiveness(livenessPercentage: number): void {
    this.currentMetrics.sovereignModeLiveness = livenessPercentage;
    
    if (livenessPercentage < this.thresholds.sovereignModeLiveness) {
      this.recordViolation('sovereignModeLiveness', this.thresholds.sovereignModeLiveness, livenessPercentage);
    }
  }

  /**
   * Get current SLA metrics
   */
  getMetrics(): SLAMetrics {
    return { ...this.currentMetrics };
  }

  /**
   * Get all violations
   */
  getViolations(): SLAViolation[] {
    return [...this.violations];
  }

  /**
   * Get active (unresolved) violations
   */
  getActiveViolations(): SLAViolation[] {
    return this.violations.filter(v => !v.resolved);
  }

  /**
   * Get SLA thresholds
   */
  getThresholds(): SLAThresholds {
    return { ...this.thresholds };
  }

  /**
   * Generate SLA compliance report
   */
  generateReport(periodStart: Date, periodEnd: Date): SLAReport {
    const periodViolations = this.violations.filter(v => 
      v.timestamp >= periodStart && v.timestamp <= periodEnd
    );

    const totalCredit = periodViolations.reduce((sum, v) => sum + v.creditPercentage, 0);
    
    let status: 'COMPLIANT' | 'WARNING' | 'BREACH' = 'COMPLIANT';
    if (periodViolations.some(v => v.severity === 'BREACH')) {
      status = 'BREACH';
    } else if (periodViolations.some(v => v.severity === 'CRITICAL' || v.severity === 'WARNING')) {
      status = 'WARNING';
    }

    const report: SLAReport = {
      reportId: `SLA-${Date.now().toString(36).toUpperCase()}`,
      periodStart,
      periodEnd,
      metrics: { ...this.currentMetrics },
      violations: periodViolations,
      totalCreditPercentage: Math.min(totalCredit, 100), // Cap at 100%
      status
    };

    slaLogger.info('Report generated', { metadata: { reportId: report.reportId, status } });
    return report;
  }

  /**
   * Resolve a violation
   */
  resolveViolation(violationId: string): boolean {
    const violation = this.violations.find(v => v.id === violationId);
    if (violation && !violation.resolved) {
      violation.resolved = true;
      violation.resolvedAt = new Date();
      slaLogger.info('Violation resolved', { metadata: { violationId } });
      return true;
    }
    return false;
  }

  /**
   * Get resilience dashboard data
   */
  getResilienceDashboard(): {
    clockDrift: { nodeId: string; driftMs: number }[];
    syncLatency: { fromNode: string; toNode: string; latencyMs: number }[];
    pqcOverhead: { cpu: number; memory: number; latencyMs: number };
    slaStatus: { metric: string; value: number; threshold: number; compliant: boolean }[];
  } {
    // HONESTY (class-B fabrication sweep 2026-07-03): clockDrift (per-node HLC drift), syncLatency
    // (per-link mesh latency) and pqcOverhead cpu/memory were fabricated via Math.random and rendered
    // as real millisecond telemetry on /resilience-monitor with NO visible label — a fail of the
    // stricter "does the user see the label?" test (the header already required these be labelled
    // SIMULATED on any surface; the client never did). No real per-node/per-link/PQC-resource
    // measurement is wired. Null them at the SOURCE (empty / not-measured) rather than emit synthetic
    // telemetry; the client renders an honest "not wired to real node telemetry" note (never a
    // false-calm empty/zero that would read as "no drift = perfect sync"). The slaStatus block below
    // is REAL (live currentMetrics vs thresholds) and is preserved unchanged. pqcOverhead.latencyMs
    // keeps the real measured value (currentMetrics.pqcHandshakeMs) or 0 — no Math.random fallback;
    // cpu/memory are 0 = not measured.
    return {
      clockDrift: [],
      syncLatency: [],
      pqcOverhead: { cpu: 0, memory: 0, latencyMs: this.currentMetrics.pqcHandshakeMs || 0 },
      slaStatus: [
        { 
          metric: 'Uptime', 
          value: this.currentMetrics.uptimePercentage, 
          threshold: this.thresholds.uptimePercentage,
          compliant: this.currentMetrics.uptimePercentage >= this.thresholds.uptimePercentage
        },
        { 
          metric: 'Sovereign Liveness', 
          value: this.currentMetrics.sovereignModeLiveness, 
          threshold: this.thresholds.sovereignModeLiveness,
          compliant: this.currentMetrics.sovereignModeLiveness >= this.thresholds.sovereignModeLiveness
        },
        { 
          metric: 'Heartbeat Detection (ms)', 
          value: this.currentMetrics.heartbeatDetectionMs, 
          threshold: this.thresholds.heartbeatDetectionMs,
          compliant: this.currentMetrics.heartbeatDetectionMs <= this.thresholds.heartbeatDetectionMs
        },
        { 
          metric: 'Mesh Sync RPO (s)', 
          value: this.currentMetrics.meshSyncRPOSeconds, 
          threshold: this.thresholds.meshSyncRPOSeconds,
          compliant: this.currentMetrics.meshSyncRPOSeconds <= this.thresholds.meshSyncRPOSeconds
        },
        { 
          metric: 'Delta-Sync RTO (ms)', 
          value: this.currentMetrics.deltaSyncRTOMs, 
          threshold: this.thresholds.deltaSyncRTOMs,
          compliant: this.currentMetrics.deltaSyncRTOMs <= this.thresholds.deltaSyncRTOMs
        },
        { 
          metric: 'Reconciliation TPS', 
          value: this.currentMetrics.reconciliationTPS, 
          threshold: this.thresholds.reconciliationTPS,
          compliant: this.currentMetrics.reconciliationTPS >= this.thresholds.reconciliationTPS
        },
        { 
          metric: 'PQC Handshake (ms)', 
          value: this.currentMetrics.pqcHandshakeMs, 
          threshold: this.thresholds.pqcHandshakeMs,
          compliant: this.currentMetrics.pqcHandshakeMs <= this.thresholds.pqcHandshakeMs
        },
        { 
          metric: 'HLC Drift (ms)', 
          value: this.currentMetrics.hlcDriftMs, 
          threshold: this.thresholds.hlcDriftMs,
          compliant: this.currentMetrics.hlcDriftMs <= this.thresholds.hlcDriftMs
        },
        { 
          metric: 'Causal Integrity (%)', 
          value: this.currentMetrics.causalIntegrityScore, 
          threshold: this.thresholds.causalIntegrityScore,
          compliant: this.currentMetrics.causalIntegrityScore >= this.thresholds.causalIntegrityScore
        }
      ]
    };
  }

  // Private methods

  private recordViolation(
    metric: keyof SLAMetrics,
    threshold: number,
    actual: number,
    forceSeverity?: 'WARNING' | 'CRITICAL' | 'BREACH'
  ): void {
    let severity: 'WARNING' | 'CRITICAL' | 'BREACH';
    let creditPercentage: number;
    let description: string;

    if (forceSeverity) {
      severity = forceSeverity;
    } else {
      // Determine severity based on deviation
      const deviation = Math.abs(actual - threshold) / threshold;
      if (deviation > 0.5) {
        severity = 'BREACH';
      } else if (deviation > 0.2) {
        severity = 'CRITICAL';
      } else {
        severity = 'WARNING';
      }
    }

    // Determine credit percentage based on metric and severity
    switch (metric) {
      case 'causalIntegrityScore':
        creditPercentage = 100; // Full credit for integrity breach
        description = 'Causal integrity breach detected - potential double-spend';
        break;
      case 'pqcHandshakeMs':
        creditPercentage = 15;
        description = `PQC handshake exceeded ${threshold}ms (actual: ${actual}ms)`;
        break;
      case 'deltaSyncRTOMs':
        creditPercentage = severity === 'BREACH' ? 25 : severity === 'CRITICAL' ? 15 : 5;
        description = `Delta-Sync RTO exceeded ${threshold}ms (actual: ${actual}ms)`;
        break;
      case 'uptimePercentage':
        creditPercentage = severity === 'BREACH' ? 50 : severity === 'CRITICAL' ? 25 : 10;
        description = `Uptime dropped below ${threshold}% (actual: ${actual}%)`;
        break;
      case 'sovereignModeLiveness':
        creditPercentage = severity === 'BREACH' ? 75 : severity === 'CRITICAL' ? 50 : 25;
        description = `Sovereign Mode liveness dropped below ${threshold}% (actual: ${actual}%)`;
        break;
      default:
        creditPercentage = severity === 'BREACH' ? 20 : severity === 'CRITICAL' ? 10 : 5;
        description = `SLA threshold violated for ${metric}: expected ${threshold}, got ${actual}`;
    }

    const violation: SLAViolation = {
      id: `VIO-${Date.now().toString(36).toUpperCase()}`,
      timestamp: new Date(),
      metric,
      threshold,
      actual,
      severity,
      creditPercentage,
      description,
      resolved: false
    };

    this.violations.push(violation);
    
    if (severity === 'BREACH') {
      slaLogger.error('SLA violation', { metadata: { severity, metric, threshold, actual, description } });
    } else {
      slaLogger.warn('SLA violation', { metadata: { severity, metric, threshold, actual, description } });
    }
    
    this.emit('violation', violation);
    
    if (severity === 'CRITICAL' || severity === 'BREACH') {
      this.emit('criticalViolation', violation);
    }
  }

  private checkAllMetrics(): void {
    // Check each metric against thresholds
    // This is called periodically during monitoring
  }

  private recordMeasurement(): void {
    this.measurementHistory.push({
      timestamp: new Date(),
      metrics: { ...this.currentMetrics }
    });

    // Keep only last 24 hours of measurements (at 5s intervals = ~17280 entries)
    const maxEntries = 17280;
    if (this.measurementHistory.length > maxEntries) {
      this.measurementHistory = this.measurementHistory.slice(-maxEntries);
    }
  }
}

// Singleton instance
let globalSLAMonitor: SLAMonitor | null = null;

export function getGlobalSLAMonitor(): SLAMonitor {
  if (!globalSLAMonitor) {
    globalSLAMonitor = new SLAMonitor();
  }
  return globalSLAMonitor;
}
