import { eventBus } from '../events/bus';
import type { FactoryEvent } from '../events/types';

// ═══════════════════════════════════════════════════════════
// WebSocket Server — Real-time event broadcasting
// ═══════════════════════════════════════════════════════════

interface WSClient {
  ws: WebSocket;
  userId: string;
  subscriptions: Set<string>;
  connectedAt: Date;
}

const clients: Map<string, WSClient> = new Map();
let clientIdCounter = 0;

/**
 * Handle WebSocket upgrade from Hono
 * Usage with @hono/node-server: handled via the upgrade callback
 */
export function handleWebSocketUpgrade(ws: WebSocket, userId: string): string {
  const clientId = `ws_${++clientIdCounter}_${Date.now().toString(36)}`;

  const client: WSClient = {
    ws,
    userId,
    subscriptions: new Set(['*']), // Subscribe to all by default
    connectedAt: new Date(),
  };

  clients.set(clientId, client);

  // Send welcome message
  ws.send(JSON.stringify({
    type: 'connected',
    clientId,
    timestamp: new Date().toISOString(),
  }));

  return clientId;
}

export function handleWebSocketMessage(clientId: string, data: string): void {
  try {
    const message = JSON.parse(data);
    const client = clients.get(clientId);
    if (!client) return;

    switch (message.type) {
      case 'subscribe':
        if (Array.isArray(message.channels)) {
          message.channels.forEach((ch: string) => client.subscriptions.add(ch));
          client.ws.send(JSON.stringify({
            type: 'subscribed',
            channels: Array.from(client.subscriptions),
          }));
        }
        break;

      case 'unsubscribe':
        if (Array.isArray(message.channels)) {
          message.channels.forEach((ch: string) => client.subscriptions.delete(ch));
          client.ws.send(JSON.stringify({
            type: 'unsubscribed',
            channels: Array.from(client.subscriptions),
          }));
        }
        break;

      case 'ping':
        client.ws.send(JSON.stringify({ type: 'pong', timestamp: new Date().toISOString() }));
        break;
    }
  } catch (err) {
    console.error('[WS] Failed to parse message:', err);
  }
}

export function handleWebSocketClose(clientId: string): void {
  clients.delete(clientId);
}

/**
 * Broadcast an event to all subscribed WebSocket clients
 */
export function broadcast(event: FactoryEvent): void {
  const payload = JSON.stringify({ type: 'event', event });

  for (const [_id, client] of clients) {
    try {
      // Check if client is subscribed to this event type
      const isSubscribed =
        client.subscriptions.has('*') ||
        client.subscriptions.has(event.type) ||
        // Check wildcard patterns like "pipeline.*"
        Array.from(client.subscriptions).some((sub) => {
          if (sub.endsWith('*')) {
            return event.type.startsWith(sub.slice(0, -1));
          }
          return false;
        });

      if (isSubscribed && client.ws.readyState === WebSocket.OPEN) {
        client.ws.send(payload);
      }
    } catch (err) {
      console.error('[WS] Failed to send to client:', err);
    }
  }
}

/**
 * Initialize: subscribe to all events and broadcast to WS clients
 */
export function initWebSocketBroadcaster(): void {
  eventBus.on('*', (event) => {
    broadcast(event);
  });
  console.log('[WS] Broadcaster initialized — forwarding all events to WebSocket clients');
}

export function getConnectedClients(): number {
  return clients.size;
}
