import { test, expect } from '@playwright/test';
import { io, type Socket } from 'socket.io-client';
import { registerUser } from '../fixtures';

/**
 * E2E tests for the NotificationsGateway WebSocket round-trip.
 *
 * Covers:
 * - JWT auth handshake on the `/notifications` namespace
 * - `notification:unread-count` pushed on connect
 * - Rejection of unauthenticated connections
 */

/** Resolve the Socket.IO base URL from the API base URL. */
function wsBaseUrl(): string {
  const apiBase = process.env['API_BASE_URL'] ?? 'http://localhost:3001/api/v1/';
  return apiBase.replace(/\/api\/v1\/?$/, '');
}

/**
 * Helper — connect to the /notifications namespace with a JWT token
 * and return a promise that resolves after the first `notification:unread-count`
 * event or rejects on timeout / connect_error.
 */
function connectSocket(token: string): Promise<{ socket: Socket; unreadCount: number }> {
  return new Promise((resolve, reject) => {
    const socket = io(`${wsBaseUrl()}/notifications`, {
      auth: { token },
      transports: ['websocket'],
      reconnection: false,
      timeout: 5000,
    });

    const timer = setTimeout(() => {
      socket.disconnect();
      reject(new Error('WS connection timed out'));
    }, 10_000);

    socket.on('notification:unread-count', (data: { unreadCount: number }) => {
      clearTimeout(timer);
      resolve({ socket, unreadCount: data.unreadCount });
    });

    socket.on('connect_error', (err) => {
      clearTimeout(timer);
      socket.disconnect();
      reject(new Error(`WS connect_error: ${err.message}`));
    });
  });
}

test.describe('Notifications WebSocket', () => {
  test('authenticated user connects and receives unread count', async ({ request }) => {
    const { accessToken } = await registerUser(request);

    const { socket, unreadCount } = await connectSocket(accessToken);
    try {
      expect(typeof unreadCount).toBe('number');
      expect(unreadCount).toBeGreaterThanOrEqual(0);
    } finally {
      socket.disconnect();
    }
  });

  test('unauthenticated connection is rejected', async () => {
    const socket = io(`${wsBaseUrl()}/notifications`, {
      auth: { token: 'invalid-token-xyz' },
      transports: ['websocket'],
      reconnection: false,
      timeout: 5000,
    });

    const disconnected = new Promise<string>((resolve) => {
      socket.on('disconnect', (reason) => resolve(reason));
      socket.on('connect_error', (err) => {
        socket.disconnect();
        resolve(`connect_error: ${err.message}`);
      });
    });

    const reason = await disconnected;
    // The gateway should disconnect or reject the connection
    expect(reason).toBeTruthy();
    socket.disconnect();
  });

  test('multi-device: two sockets for same user both receive unread count', async ({
    request,
  }) => {
    const { accessToken } = await registerUser(request);

    const [conn1, conn2] = await Promise.all([
      connectSocket(accessToken),
      connectSocket(accessToken),
    ]);

    try {
      expect(conn1.unreadCount).toBeGreaterThanOrEqual(0);
      expect(conn2.unreadCount).toBeGreaterThanOrEqual(0);
    } finally {
      conn1.socket.disconnect();
      conn2.socket.disconnect();
    }
  });
});
