/**
 * Clock synchronization and skew handling
 */

import { ClockSyncConfig } from './types';
import { DEFAULT_CONFIG } from './constants';

export class ClockSyncManager {
  private config: ClockSyncConfig;
  private lastSyncTime = 0;
  private serverOffset = 0; // Server time - client time
  private syncInterval?: NodeJS.Timeout | undefined;
  
  constructor(config: Partial<ClockSyncConfig> = {}) {
    this.config = {
      serverTimeSource: config.serverTimeSource ?? 'ntp',
      ntpServers: config.ntpServers ?? ['pool.ntp.org', 'time.google.com'],
      maxClockSkewSeconds: config.maxClockSkewSeconds ?? DEFAULT_CONFIG.maxClockSkewSeconds,
      skewCheckIntervalMs: config.skewCheckIntervalMs ?? DEFAULT_CONFIG.skewCheckIntervalMs,
      jwtClockSkewTolerance: config.jwtClockSkewTolerance ?? DEFAULT_CONFIG.jwtClockSkewTolerance,
      jwtMaxAge: config.jwtMaxAge ?? DEFAULT_CONFIG.jwtMaxAge
    };
  }
  
  async syncWithServer(apiServer: string, jwtToken?: string): Promise<void> {
    try {
      // Get server time from API
      const response = await fetch(`${apiServer}/api/v2/time`, {
        method: 'GET',
        headers: jwtToken ? { 'Authorization': `Bearer ${jwtToken}` } : {}
      });
      
      if (!response.ok) {
        throw new Error(`Clock sync failed: HTTP ${response.status}`);
      }
      
      const serverTime = parseInt(response.headers.get('X-Server-Time') || '0');
      const clientTime = Date.now();
      
      if (serverTime === 0) {
        throw new Error('Invalid server time response');
      }
      
      this.serverOffset = serverTime - clientTime;
      this.lastSyncTime = clientTime;
      
      // Validate skew is within tolerance
      if (Math.abs(this.serverOffset) > this.config.maxClockSkewSeconds * 1000) {
        // Large clock skew detected: ${this.serverOffset}ms
      }
      
      // Clock sync successful: offset=${this.serverOffset}ms
      
    } catch (error) {
      // Clock sync failed: ${error}
      // Continue with client time, but log the issue
    }
  }
  
  getServerTime(): number {
    return Date.now() + this.serverOffset;
  }
  
  getClientTime(): number {
    return Date.now();
  }
  
  getServerOffset(): number {
    return this.serverOffset;
  }
  
  getLastSyncTime(): number {
    return this.lastSyncTime;
  }
  
  validateJwtTimestamp(jwt: Record<string, unknown>): boolean {
    const now = this.getServerTime();
    const iat = (jwt.iat as number) * 1000; // Convert to milliseconds
    const exp = (jwt.exp as number) * 1000;
    
    // Check if JWT is within valid time window
    const skewTolerance = this.config.jwtClockSkewTolerance * 1000;
    const maxAge = this.config.jwtMaxAge;
    
    const isValid = (iat <= now + skewTolerance) && // JWT should not be issued too far in the past
                   (iat >= now - skewTolerance) && // JWT should not be issued too far in the future
                   (now <= exp + skewTolerance) && 
                   (now - iat <= maxAge);
    
    if (!isValid) {
      // JWT timestamp validation failed: ${JSON.stringify({now, iat, exp, skewTolerance, maxAge, serverOffset: this.serverOffset})}
    }
    
    return isValid;
  }
  
  isClockSkewExcessive(): boolean {
    return Math.abs(this.serverOffset) > this.config.maxClockSkewSeconds * 1000;
  }
  
  needsSync(): boolean {
    const timeSinceLastSync = Date.now() - this.lastSyncTime;
    return timeSinceLastSync > this.config.skewCheckIntervalMs;
  }
  
  // Periodic sync
  startPeriodicSync(apiServer: string, jwtToken?: string): void {
    if (this.syncInterval) {
      clearInterval(this.syncInterval);
    }
    
    this.syncInterval = setInterval(() => {
      this.syncWithServer(apiServer, jwtToken);
    }, this.config.skewCheckIntervalMs);
  }
  
  stopPeriodicSync(): void {
    if (this.syncInterval) {
      clearInterval(this.syncInterval);
      this.syncInterval = undefined;
    }
  }
  
  getConfig(): ClockSyncConfig {
    return { ...this.config };
  }
}

/**
 * Create default clock sync manager
 */
export function createDefaultClockSyncManager(): ClockSyncManager {
  return new ClockSyncManager();
}

/**
 * Create clock sync manager with custom config
 */
export function createClockSyncManager(config: Partial<ClockSyncConfig>): ClockSyncManager {
  return new ClockSyncManager(config);
}

/**
 * Clock sync configuration presets
 */
export const CLOCK_SYNC_PRESETS = {
  // Conservative: Frequent sync, strict tolerance
  conservative: {
    maxClockSkewSeconds: 15,
    skewCheckIntervalMs: 180000, // 3 minutes
    jwtClockSkewTolerance: 15,
    jwtMaxAge: 1800000 // 30 minutes
  },
  
  // Balanced: Good balance of sync frequency and tolerance
  balanced: {
    maxClockSkewSeconds: 30,
    skewCheckIntervalMs: 300000, // 5 minutes
    jwtClockSkewTolerance: 30,
    jwtMaxAge: 3600000 // 1 hour
  },
  
  // Relaxed: Less frequent sync, more tolerance
  relaxed: {
    maxClockSkewSeconds: 60,
    skewCheckIntervalMs: 600000, // 10 minutes
    jwtClockSkewTolerance: 60,
    jwtMaxAge: 7200000 // 2 hours
  }
} as const;
