/**
 * QM Session Manager - Centralized session state management
 */

import { getRandomUserAgent } from '@stock-bot/utils';
import { QM_SESSION_IDS, SESSION_CONFIG } from './config';
import type { QMSession } from './types';

export class QMSessionManager {
  private static instance: QMSessionManager | null = null;
  private sessionCache: Record<string, QMSession[]> = {};
  private isInitialized = false;

  private constructor() {
    // Initialize session cache with known session IDs
    Object.values(QM_SESSION_IDS).forEach(sessionId => {
      this.sessionCache[sessionId] = [];
    });
  }

  static getInstance(): QMSessionManager {
    if (!QMSessionManager.instance) {
      QMSessionManager.instance = new QMSessionManager();
    }
    return QMSessionManager.instance;
  }

  /**
   * Get a random session for the given session ID
   */
  getSession(sessionId: string): QMSession | null {
    const sessions = this.sessionCache[sessionId];
    if (!sessions || sessions.length === 0) {
      return null;
    }

    // Filter out sessions with excessive failures
    const validSessions = sessions.filter(
      session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
    );
    if (validSessions.length === 0) {
      return null;
    }

    return validSessions[Math.floor(Math.random() * validSessions.length)];
  }

  /**
   * Add a session to the cache
   */
  addSession(sessionId: string, session: QMSession): void {
    if (!this.sessionCache[sessionId]) {
      this.sessionCache[sessionId] = [];
    }
    this.sessionCache[sessionId].push(session);
  }

  /**
   * Get all sessions for a session ID
   */
  getSessions(sessionId: string): QMSession[] {
    return this.sessionCache[sessionId] || [];
  }

  /**
   * Get session count for all session IDs
   */
  getSessionCount(): number {
    return Object.values(this.sessionCache).reduce((total, sessions) => total + sessions.length, 0);
  }

  /**
   * Clean up failed sessions
   */
  cleanupFailedSessions(): number {
    let removedCount = 0;

    Object.keys(this.sessionCache).forEach(sessionId => {
      const initialCount = this.sessionCache[sessionId].length;
      this.sessionCache[sessionId] = this.sessionCache[sessionId].filter(
        session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
      );
      removedCount += initialCount - this.sessionCache[sessionId].length;
    });

    return removedCount;
  }

  getQmHeaders(): Record<string, string> {
    return {
      'User-Agent': getRandomUserAgent(),
      Accept: '*/*',
      'Accept-Language': 'en',
      'Sec-Fetch-Mode': 'cors',
      Origin: 'https://www.quotemedia.com',
      Referer: 'https://www.quotemedia.com/',
    };
  }

  /**
   * Check if more sessions are needed for a session ID
   */
  needsMoreSessions(sessionId: string): boolean {
    const sessions = this.sessionCache[sessionId] || [];
    const validSessions = sessions.filter(
      session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
    );
    return validSessions.length < SESSION_CONFIG.MIN_SESSIONS;
  }

  /**
   * Check if session ID is at capacity
   */
  isAtCapacity(sessionId: string): boolean {
    const sessions = this.sessionCache[sessionId] || [];
    return sessions.length >= SESSION_CONFIG.MAX_SESSIONS;
  }

  /**
   * Get session cache statistics
   */
  getStats() {
    const stats: Record<string, { total: number; valid: number; failed: number }> = {};

    Object.entries(this.sessionCache).forEach(([sessionId, sessions]) => {
      const validSessions = sessions.filter(
        session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
      );
      const failedSessions = sessions.filter(
        session => session.failedCalls > SESSION_CONFIG.MAX_FAILED_CALLS
      );

      stats[sessionId] = {
        total: sessions.length,
        valid: validSessions.length,
        failed: failedSessions.length,
      };
    });

    return stats;
  }

  /**
   * Mark manager as initialized
   */
  setInitialized(initialized: boolean = true): void {
    this.isInitialized = initialized;
  }

  /**
   * Check if manager is initialized
   */
  getInitialized(): boolean {
    return this.isInitialized;
  }
}
