/**
 * WebSocket Admin Service
 * Handles real-time communication with admin clients
 */

import { EventEmitter } from 'events';

export interface AdminClient {
  sessionId: string;
  adminId: number;
  adminEmail: string;
  connectedAt: Date;
  lastActivity: Date;
  isActive: boolean;
  subscriptions: Set<string>; // Event types subscribed to
}

export interface WebSocketMessage {
  type: 'subscribe' | 'unsubscribe' | 'acknowledge' | 'action';
  eventType?: string;
  data?: Record<string, any>;
  messageId?: string;
}

export interface BroadcastMessage {
  type: string;
  timestamp: Date;
  data: Record<string, any>;
  severity?: 'low' | 'medium' | 'high' | 'critical';
}

/**
 * WebSocket Admin Service
 */
export class WebSocketAdminService extends EventEmitter {
  private clients: Map<string, AdminClient> = new Map();
  private messageQueue: Map<string, BroadcastMessage[]> = new Map();
  private maxQueueSize: number = 100;

  constructor() {
    super();
  }

  /**
   * Register a new admin client
   */
  registerClient(sessionId: string, adminId: number, adminEmail: string): AdminClient {
    const client: AdminClient = {
      sessionId,
      adminId,
      adminEmail,
      connectedAt: new Date(),
      lastActivity: new Date(),
      isActive: true,
      subscriptions: new Set(['withdrawal_request', 'fraud_alert', 'system_alert']), // Default subscriptions
    };

    this.clients.set(sessionId, client);
    this.emit('client_connected', client);

    console.log(`[WebSocket] Admin ${adminEmail} connected (${sessionId})`);

    return client;
  }

  /**
   * Unregister a client
   */
  unregisterClient(sessionId: string): boolean {
    const client = this.clients.get(sessionId);
    if (!client) return false;

    client.isActive = false;
    this.clients.delete(sessionId);
    this.emit('client_disconnected', client);

    console.log(`[WebSocket] Admin ${client.adminEmail} disconnected`);

    return true;
  }

  /**
   * Update client activity
   */
  updateClientActivity(sessionId: string): boolean {
    const client = this.clients.get(sessionId);
    if (!client) return false;

    client.lastActivity = new Date();
    return true;
  }

  /**
   * Subscribe client to event type
   */
  subscribeClient(sessionId: string, eventType: string): boolean {
    const client = this.clients.get(sessionId);
    if (!client) return false;

    client.subscriptions.add(eventType);
    this.emit('client_subscribed', { sessionId, eventType });

    return true;
  }

  /**
   * Unsubscribe client from event type
   */
  unsubscribeClient(sessionId: string, eventType: string): boolean {
    const client = this.clients.get(sessionId);
    if (!client) return false;

    client.subscriptions.delete(eventType);
    this.emit('client_unsubscribed', { sessionId, eventType });

    return true;
  }

  /**
   * Broadcast message to all subscribed clients
   */
  broadcast(message: BroadcastMessage): void {
    const subscribers = Array.from(this.clients.values()).filter(
      (client) => client.isActive && client.subscriptions.has(message.type)
    );

    console.log(
      `[WebSocket] Broadcasting ${message.type} to ${subscribers.length} clients`
    );

    subscribers.forEach((client) => {
      this.emit('send_message', {
        sessionId: client.sessionId,
        message,
      });
    });

    // Queue message for offline clients
    this.queueMessage(message);
  }

  /**
   * Send message to specific client
   */
  sendToClient(sessionId: string, message: BroadcastMessage): boolean {
    const client = this.clients.get(sessionId);
    if (!client || !client.isActive) return false;

    this.emit('send_message', {
      sessionId,
      message,
    });

    return true;
  }

  /**
   * Queue message for offline clients
   */
  private queueMessage(message: BroadcastMessage): void {
    const key = message.type;

    if (!this.messageQueue.has(key)) {
      this.messageQueue.set(key, []);
    }

    const queue = this.messageQueue.get(key)!;
    queue.push(message);

    // Maintain queue size
    if (queue.length > this.maxQueueSize) {
      queue.shift();
    }
  }

  /**
   * Get queued messages for client
   */
  getQueuedMessages(sessionId: string): BroadcastMessage[] {
    const client = this.clients.get(sessionId);
    if (!client) return [];

    const messages: BroadcastMessage[] = [];

    for (const [eventType, queue] of this.messageQueue.entries()) {
      if (client.subscriptions.has(eventType)) {
        messages.push(...queue);
      }
    }

    return messages.sort(
      (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
    );
  }

  /**
   * Get active clients count
   */
  getActiveClientsCount(): number {
    return Array.from(this.clients.values()).filter((c) => c.isActive).length;
  }

  /**
   * Get all active clients
   */
  getActiveClients(): AdminClient[] {
    return Array.from(this.clients.values()).filter((c) => c.isActive);
  }

  /**
   * Get client by session ID
   */
  getClient(sessionId: string): AdminClient | undefined {
    return this.clients.get(sessionId);
  }

  /**
   * Broadcast withdrawal event
   */
  broadcastWithdrawalEvent(
    eventType: 'withdrawal_request' | 'withdrawal_approved' | 'withdrawal_rejected' | 'withdrawal_completed',
    data: {
      withdrawalId: string;
      playerId: number;
      playerName: string;
      amount: number;
      currency: string;
      status: string;
    }
  ): void {
    this.broadcast({
      type: eventType,
      timestamp: new Date(),
      data,
      severity: eventType === 'withdrawal_request' ? 'medium' : 'low',
    });
  }

  /**
   * Broadcast fraud alert
   */
  broadcastFraudAlert(data: {
    alertId: string;
    playerId: number;
    playerName: string;
    transactionId: string;
    amount: number;
    riskLevel: string;
    indicators: string[];
  }): void {
    this.broadcast({
      type: 'fraud_alert',
      timestamp: new Date(),
      data,
      severity: data.riskLevel === 'critical' ? 'critical' : 'high',
    });
  }

  /**
   * Broadcast metrics update
   */
  broadcastMetrics(data: {
    totalRevenue: number;
    totalTransactions: number;
    averageTransactionValue: number;
    pendingWithdrawals: number;
    pendingWithdrawalAmount: number;
    flaggedTransactions: number;
  }): void {
    this.broadcast({
      type: 'metrics_update',
      timestamp: new Date(),
      data,
      severity: 'low',
    });
  }

  /**
   * Broadcast system alert
   */
  broadcastSystemAlert(data: {
    title: string;
    message: string;
    severity: 'low' | 'medium' | 'high' | 'critical';
  }): void {
    this.broadcast({
      type: 'system_alert',
      timestamp: new Date(),
      data,
      severity: data.severity,
    });
  }

  /**
   * Get statistics
   */
  getStatistics(): {
    totalClients: number;
    activeClients: number;
    inactiveClients: number;
    messageQueueSize: number;
  } {
    const allClients = Array.from(this.clients.values());
    const activeClients = allClients.filter((c) => c.isActive);
    const inactiveClients = allClients.filter((c) => !c.isActive);

    let messageQueueSize = 0;
    for (const queue of this.messageQueue.values()) {
      messageQueueSize += queue.length;
    }

    return {
      totalClients: allClients.length,
      activeClients: activeClients.length,
      inactiveClients: inactiveClients.length,
      messageQueueSize,
    };
  }

  /**
   * Clear inactive clients
   */
  clearInactiveClients(inactiveForMinutes: number = 30): number {
    const cutoffTime = new Date(Date.now() - inactiveForMinutes * 60 * 1000);
    let count = 0;

    for (const [sessionId, client] of this.clients.entries()) {
      if (!client.isActive && client.lastActivity < cutoffTime) {
        this.clients.delete(sessionId);
        count++;
      }
    }

    return count;
  }
}

/**
 * Global WebSocket service instance
 */
let websocketService: WebSocketAdminService | null = null;

/**
 * Get or create WebSocket service
 */
export function getWebSocketAdminService(): WebSocketAdminService {
  if (!websocketService) {
    websocketService = new WebSocketAdminService();
  }
  return websocketService;
}
