import { WebSocketServer, WebSocket } from 'ws';
import { Server } from 'http';
import { verify } from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

interface AdminClient {
  ws: WebSocket;
  adminId: number;
  role: string;
  connectedAt: Date;
}

class AdminNotificationBroadcaster {
  private wss: WebSocketServer | null = null;
  private clients: Map<number, AdminClient> = new Map();
  private eventSubscriptions: Map<string, Set<number>> = new Map();

  /**
   * Initialize WebSocket server
   */
  initialize(server: Server) {
    this.wss = new WebSocketServer({ 
      server,
      path: '/api/admin/notifications',
      perMessageDeflate: false,
    });

    this.wss.on('connection', (ws: WebSocket, req) => {
      this.handleConnection(ws, req);
    });

    console.log('[WebSocket] Admin notification server initialized');
  }

  /**
   * Handle new WebSocket connection
   */
  private handleConnection(ws: WebSocket, req: any) {
    const token = this.extractToken(req);
    
    if (!token) {
      ws.close(1008, 'Unauthorized: No token provided');
      return;
    }

    try {
      const decoded = verify(token, JWT_SECRET) as any;
      const adminId = decoded.userId;
      const role = decoded.role;

      // Only allow admins
      if (role !== 'admin') {
        ws.close(1008, 'Unauthorized: Admin role required');
        return;
      }

      const client: AdminClient = {
        ws,
        adminId,
        role,
        connectedAt: new Date(),
      };

      this.clients.set(adminId, client);
      console.log(`[WebSocket] Admin ${adminId} connected`);

      // Send connection confirmation
      ws.send(JSON.stringify({
        type: 'connection',
        status: 'connected',
        adminId,
        timestamp: new Date().toISOString(),
      }));

      // Handle messages
      ws.on('message', (data: Buffer) => {
        this.handleMessage(adminId, data);
      });

      // Handle disconnect
      ws.on('close', () => {
        this.clients.delete(adminId);
        console.log(`[WebSocket] Admin ${adminId} disconnected`);
      });

      // Handle errors
      ws.on('error', (error) => {
        console.error(`[WebSocket] Error for admin ${adminId}:`, error);
      });

    } catch (error) {
      console.error('[WebSocket] Token verification failed:', error);
      ws.close(1008, 'Unauthorized: Invalid token');
    }
  }

  /**
   * Handle incoming WebSocket messages
   */
  private handleMessage(adminId: number, data: Buffer) {
    try {
      const message = JSON.parse(data.toString());
      
      switch (message.type) {
        case 'subscribe':
          this.subscribe(adminId, message.events);
          break;
        case 'unsubscribe':
          this.unsubscribe(adminId, message.events);
          break;
        case 'ping':
          const client = this.clients.get(adminId);
          if (client) {
            client.ws.send(JSON.stringify({ type: 'pong', timestamp: new Date().toISOString() }));
          }
          break;
      }
    } catch (error) {
      console.error('[WebSocket] Error processing message:', error);
    }
  }

  /**
   * Subscribe admin to notification events
   */
  private subscribe(adminId: number, events: string[]) {
    for (const event of events) {
      if (!this.eventSubscriptions.has(event)) {
        this.eventSubscriptions.set(event, new Set());
      }
      this.eventSubscriptions.get(event)!.add(adminId);
    }
    console.log(`[WebSocket] Admin ${adminId} subscribed to events:`, events);
  }

  /**
   * Unsubscribe admin from notification events
   */
  private unsubscribe(adminId: number, events: string[]) {
    for (const event of events) {
      this.eventSubscriptions.get(event)?.delete(adminId);
    }
  }

  /**
   * Broadcast notification to specific admin(s)
   */
  broadcast(notification: {
    type: string;
    title: string;
    message: string;
    severity: 'low' | 'medium' | 'high' | 'critical';
    data?: any;
    targetAdminIds?: number[];
  }) {
    const payload = {
      type: 'notification',
      notification,
      timestamp: new Date().toISOString(),
    };

    const targetAdmins = notification.targetAdminIds || 
      Array.from(this.eventSubscriptions.get(notification.type) || []);

    for (const adminId of targetAdmins) {
      const client = this.clients.get(adminId);
      if (client && client.ws.readyState === WebSocket.OPEN) {
        client.ws.send(JSON.stringify(payload));
      }
    }

    console.log(`[WebSocket] Broadcasted ${notification.type} to ${targetAdmins.length} admins`);
  }

  /**
   * Broadcast to all connected admins
   */
  broadcastToAll(notification: {
    type: string;
    title: string;
    message: string;
    severity: 'low' | 'medium' | 'high' | 'critical';
    data?: any;
  }) {
    const payload = {
      type: 'notification',
      notification,
      timestamp: new Date().toISOString(),
    };

    let count = 0;
    for (const client of this.clients.values()) {
      if (client.ws.readyState === WebSocket.OPEN) {
        client.ws.send(JSON.stringify(payload));
        count++;
      }
    }

    console.log(`[WebSocket] Broadcasted ${notification.type} to all ${count} connected admins`);
  }

  /**
   * Get connected admin count
   */
  getConnectedCount(): number {
    return this.clients.size;
  }

  /**
   * Extract JWT token from WebSocket upgrade request
   */
  private extractToken(req: any): string | null {
    const url = new URL(req.url || '', 'http://localhost');
    return url.searchParams.get('token');
  }
}

export const adminNotificationBroadcaster = new AdminNotificationBroadcaster();
