import { Server as HttpServer } from 'http';
import WebSocket from 'ws';
import { ILogger } from '../../application/ports/logger';
import { WebSocketNotificationGateway } from './websocket-gateway';

export interface WebSocketServerOptions {
  httpServer: HttpServer;
  path?: string;
  logger: ILogger;
}

export interface WebSocketServerResult {
  gateway: WebSocketNotificationGateway;
  close: () => Promise<void>;
}

/**
 * Attaches a WebSocket server to the existing HTTP server and wires it to a
 * notification gateway. Returns the gateway so the application layer can use it
 * to send messages to connected devices, plus a close helper for graceful
 * shutdown.
 */
export const createWebSocketServer = (options: WebSocketServerOptions): WebSocketServerResult => {
  const { httpServer, path = '/ws', logger } = options;

  const gateway = new WebSocketNotificationGateway(logger);

  const wss = new WebSocket.Server({ server: httpServer, path });

  wss.on('connection', (socket) => {
    gateway.handleConnection(socket);
  });

  wss.on('error', (error) => {
    logger.error('[WebSocket] Server error', { error });
  });

  logger.info('[WebSocket] Server attached', { path });

  let isClosing = false;
  let closePromise: Promise<void> | undefined;

  const close = (): Promise<void> => {
    if (isClosing) {
      return closePromise ?? Promise.resolve();
    }
    isClosing = true;

    closePromise = new Promise((resolve) => {
      let timeout: NodeJS.Timeout | undefined;

      const cleanup = (): void => {
        if (timeout) {
          clearTimeout(timeout);
          timeout = undefined;
        }
        wss.off('close', onClosed);
      };

      const onClosed = (): void => {
        cleanup();
        resolve();
      };

      timeout = setTimeout(() => {
        logger.warn('[WebSocket] Graceful close timed out; terminating remaining clients');
        for (const client of wss.clients) {
          client.terminate();
        }
        onClosed();
      }, 3_000);

      wss.once('close', onClosed);

      try {
        wss.close();
      } catch (error) {
        logger.warn('[WebSocket] Error while closing server', { error });
        cleanup();
        resolve();
      }
    });

    return closePromise;
  };

  return { gateway, close };
};
