/**
 * TimeSafari Community Features Integration
 * 
 * Implements community features integration with rate limiting, backoff policies,
 * and polling optimization for TimeSafari's community-driven platform.
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

import { 
  TimeSafariIntegrationService,
  TimeSafariUserConfig,
  TimeSafariNotificationBundle
} from './timesafari-integration';

/**
 * TimeSafari Community Integration Service
 * 
 * Handles community features integration with intelligent rate limiting,
 * backoff policies, and polling optimization.
 */
export class TimeSafariCommunityIntegrationService {
  private static instance: TimeSafariCommunityIntegrationService;
  private integrationService: TimeSafariIntegrationService;
  private rateLimiter: RateLimiter;
  private backoffManager: BackoffManager;
  private pollingManager: PollingManager;

  private constructor() {
    this.integrationService = TimeSafariIntegrationService.getInstance();
    this.rateLimiter = new RateLimiter();
    this.backoffManager = new BackoffManager();
    this.pollingManager = new PollingManager();
  }

  /**
   * Get singleton instance
   */
  static getInstance(): TimeSafariCommunityIntegrationService {
    if (!TimeSafariCommunityIntegrationService.instance) {
      TimeSafariCommunityIntegrationService.instance = new TimeSafariCommunityIntegrationService();
    }
    return TimeSafariCommunityIntegrationService.instance;
  }

  /**
   * Initialize community integration
   * 
   * @param config - Community integration configuration
   */
  async initialize(config: CommunityIntegrationConfig): Promise<void> {
    try {
      // Initializing TimeSafari community integration

      // Initialize rate limiter
      this.rateLimiter.configure({
        maxRequestsPerMinute: config.maxRequestsPerMinute || 30,
        maxRequestsPerHour: config.maxRequestsPerHour || 1000,
        burstLimit: config.burstLimit || 10
      });

      // Initialize backoff manager
      this.backoffManager.configure({
        initialDelayMs: config.initialBackoffMs || 1000,
        maxDelayMs: config.maxBackoffMs || 30000,
        backoffMultiplier: config.backoffMultiplier || 2,
        jitterEnabled: config.jitterEnabled !== false
      });

      // Initialize polling manager
      this.pollingManager.configure({
        baseIntervalMs: config.basePollingIntervalMs || 300000, // 5 minutes
        maxIntervalMs: config.maxPollingIntervalMs || 1800000, // 30 minutes
        adaptivePolling: config.adaptivePolling !== false
      });

      // TimeSafari community integration initialization complete
    } catch (error) {
      throw new Error(`TimeSafari community integration initialization failed: ${error}`);
    }
  }

  /**
   * Fetch community data with rate limiting and backoff
   * 
   * @param config - User configuration
   * @returns Community data bundle
   */
  async fetchCommunityDataWithRateLimit(config: TimeSafariUserConfig): Promise<TimeSafariNotificationBundle> {
    try {
      // Fetching community data with rate limiting

      // Check rate limits
      if (!this.rateLimiter.canMakeRequest()) {
        const waitTime = this.rateLimiter.getWaitTime();
        // Rate limit exceeded, waiting
        await this.sleep(waitTime);
      }

      // Check backoff status
      if (this.backoffManager.isInBackoff()) {
        const backoffTime = this.backoffManager.getRemainingBackoffTime();
        // In backoff, waiting
        await this.sleep(backoffTime);
      }

      // Record request attempt
      this.rateLimiter.recordRequest();

      try {
        // Fetch data
        const bundle = await this.integrationService.fetchCommunityData(config);
        
        // Reset backoff on success
        this.backoffManager.recordSuccess();
        
        // Update polling interval based on success
        this.pollingManager.recordSuccess();
        
        // Community data fetch successful
        return bundle;

      } catch (error) {
        // Record failure and apply backoff
        this.backoffManager.recordFailure();
        this.pollingManager.recordFailure();
        
        throw new Error(`Community data fetch failed: ${error}`);
      }

    } catch (error) {
      throw new Error(`Rate-limited fetch failed: ${error}`);
    }
  }

  /**
   * Get optimized polling interval
   * 
   * @returns Polling interval in milliseconds
   */
  getOptimizedPollingInterval(): number {
    return this.pollingManager.getCurrentInterval();
  }

  /**
   * Get rate limit status
   * 
   * @returns Rate limit status
   */
  getRateLimitStatus(): RateLimitStatus {
    return this.rateLimiter.getStatus();
  }

  /**
   * Get backoff status
   * 
   * @returns Backoff status
   */
  getBackoffStatus(): BackoffStatus {
    return this.backoffManager.getStatus();
  }

  /**
   * Get polling status
   * 
   * @returns Polling status
   */
  getPollingStatus(): PollingStatus {
    return this.pollingManager.getStatus();
  }

  /**
   * Sleep utility
   * 
   * @param ms - Milliseconds to sleep
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

/**
 * Rate Limiter
 */
class RateLimiter {
  private config: RateLimitConfig = {
    maxRequestsPerMinute: 30,
    maxRequestsPerHour: 1000,
    burstLimit: 10
  };
  private requests: number[] = [];
  private burstRequests: number[] = [];

  configure(config: RateLimitConfig): void {
    this.config = { ...this.config, ...config };
  }

  canMakeRequest(): boolean {
    const now = Date.now();
    const oneMinuteAgo = now - 60000;
    const oneHourAgo = now - 3600000;
    const oneSecondAgo = now - 1000;

    // Clean old requests
    this.requests = this.requests.filter(time => time > oneHourAgo);
    this.burstRequests = this.burstRequests.filter(time => time > oneSecondAgo);

    // Check limits
    const requestsLastMinute = this.requests.filter(time => time > oneMinuteAgo).length;
    const requestsLastHour = this.requests.length;
    const burstRequests = this.burstRequests.length;

    return requestsLastMinute < this.config.maxRequestsPerMinute &&
           requestsLastHour < this.config.maxRequestsPerHour &&
           burstRequests < this.config.burstLimit;
  }

  recordRequest(): void {
    const now = Date.now();
    this.requests.push(now);
    this.burstRequests.push(now);
  }

  getWaitTime(): number {
    const now = Date.now();
    const oneMinuteAgo = now - 60000;
    const oneHourAgo = now - 3600000;
    const oneSecondAgo = now - 1000;

    // Find oldest request in each window
    const requestsLastMinute = this.requests.filter(time => time > oneMinuteAgo);
    const requestsLastHour = this.requests.filter(time => time > oneHourAgo);
    const burstRequests = this.burstRequests.filter(time => time > oneSecondAgo);

    let waitTime = 0;

    // Check minute limit
    if (requestsLastMinute.length >= this.config.maxRequestsPerMinute) {
      const oldestInMinute = Math.min(...requestsLastMinute);
      waitTime = Math.max(waitTime, oldestInMinute + 60000 - now);
    }

    // Check hour limit
    if (requestsLastHour.length >= this.config.maxRequestsPerHour) {
      const oldestInHour = Math.min(...requestsLastHour);
      waitTime = Math.max(waitTime, oldestInHour + 3600000 - now);
    }

    // Check burst limit
    if (burstRequests.length >= this.config.burstLimit) {
      const oldestBurst = Math.min(...burstRequests);
      waitTime = Math.max(waitTime, oldestBurst + 1000 - now);
    }

    return Math.max(0, waitTime);
  }

  getStatus(): RateLimitStatus {
    const now = Date.now();
    const oneMinuteAgo = now - 60000;
    const oneHourAgo = now - 3600000;
    const oneSecondAgo = now - 1000;

    return {
      requestsLastMinute: this.requests.filter(time => time > oneMinuteAgo).length,
      requestsLastHour: this.requests.filter(time => time > oneHourAgo).length,
      burstRequests: this.burstRequests.filter(time => time > oneSecondAgo).length,
      canMakeRequest: this.canMakeRequest(),
      waitTime: this.getWaitTime()
    };
  }
}

/**
 * Backoff Manager
 */
class BackoffManager {
  private config: BackoffConfig = {
    initialDelayMs: 1000,
    maxDelayMs: 30000,
    backoffMultiplier: 2,
    jitterEnabled: true
  };
  private failureCount = 0;
  private lastFailureTime = 0;
  private currentDelay = 0;

  configure(config: BackoffConfig): void {
    this.config = { ...this.config, ...config };
  }

  recordSuccess(): void {
    this.failureCount = 0;
    this.currentDelay = 0;
  }

  recordFailure(): void {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    
    // Calculate exponential backoff
    this.currentDelay = Math.min(
      this.config.initialDelayMs * Math.pow(this.config.backoffMultiplier, this.failureCount - 1),
      this.config.maxDelayMs
    );

    // Add jitter if enabled
    if (this.config.jitterEnabled) {
      const jitter = Math.random() * 0.1 * this.currentDelay;
      this.currentDelay += jitter;
    }
  }

  isInBackoff(): boolean {
    if (this.failureCount === 0) return false;
    
    const timeSinceLastFailure = Date.now() - this.lastFailureTime;
    return timeSinceLastFailure < this.currentDelay;
  }

  getRemainingBackoffTime(): number {
    if (!this.isInBackoff()) return 0;
    
    const timeSinceLastFailure = Date.now() - this.lastFailureTime;
    return this.currentDelay - timeSinceLastFailure;
  }

  getStatus(): BackoffStatus {
    return {
      failureCount: this.failureCount,
      currentDelay: this.currentDelay,
      isInBackoff: this.isInBackoff(),
      remainingBackoffTime: this.getRemainingBackoffTime()
    };
  }
}

/**
 * Polling Manager
 */
class PollingManager {
  private config: PollingConfig = {
    baseIntervalMs: 300000, // 5 minutes
    maxIntervalMs: 1800000, // 30 minutes
    adaptivePolling: true
  };
  private currentInterval = 300000;
  private consecutiveFailures = 0;
  private consecutiveSuccesses = 0;

  configure(config: PollingConfig): void {
    this.config = { ...this.config, ...config };
    this.currentInterval = this.config.baseIntervalMs;
  }

  recordSuccess(): void {
    this.consecutiveSuccesses++;
    this.consecutiveFailures = 0;
    
    if (this.config.adaptivePolling && this.consecutiveSuccesses >= 3) {
      // Decrease interval on sustained success
      this.currentInterval = Math.max(
        this.config.baseIntervalMs,
        this.currentInterval * 0.8
      );
    }
  }

  recordFailure(): void {
    this.consecutiveFailures++;
    this.consecutiveSuccesses = 0;
    
    if (this.config.adaptivePolling) {
      // Increase interval on failure
      this.currentInterval = Math.min(
        this.config.maxIntervalMs,
        this.currentInterval * 1.5
      );
    }
  }

  getCurrentInterval(): number {
    return this.currentInterval;
  }

  getStatus(): PollingStatus {
    return {
      currentInterval: this.currentInterval,
      consecutiveFailures: this.consecutiveFailures,
      consecutiveSuccesses: this.consecutiveSuccesses,
      adaptivePolling: this.config.adaptivePolling
    };
  }
}

/**
 * Configuration Interfaces
 */
export interface CommunityIntegrationConfig {
  maxRequestsPerMinute?: number;
  maxRequestsPerHour?: number;
  burstLimit?: number;
  initialBackoffMs?: number;
  maxBackoffMs?: number;
  backoffMultiplier?: number;
  jitterEnabled?: boolean;
  basePollingIntervalMs?: number;
  maxPollingIntervalMs?: number;
  adaptivePolling?: boolean;
}

interface RateLimitConfig {
  maxRequestsPerMinute: number;
  maxRequestsPerHour: number;
  burstLimit: number;
}

interface BackoffConfig {
  initialDelayMs: number;
  maxDelayMs: number;
  backoffMultiplier: number;
  jitterEnabled: boolean;
}

interface PollingConfig {
  baseIntervalMs: number;
  maxIntervalMs: number;
  adaptivePolling: boolean;
}

/**
 * Status Interfaces
 */
export interface RateLimitStatus {
  requestsLastMinute: number;
  requestsLastHour: number;
  burstRequests: number;
  canMakeRequest: boolean;
  waitTime: number;
}

export interface BackoffStatus {
  failureCount: number;
  currentDelay: number;
  isInBackoff: boolean;
  remainingBackoffTime: number;
}

export interface PollingStatus {
  currentInterval: number;
  consecutiveFailures: number;
  consecutiveSuccesses: number;
  adaptivePolling: boolean;
}
