import { Test, TestingModule } from '@nestjs/testing';
import { WebSocketGateway } from './websocket.gateway';
import { Server, Socket } from 'socket.io';
import { describe, it, expect, vi, beforeEach } from 'vitest';

interface AuthenticatedSocket extends Socket {
  data: {
    userId: string;
    workspaceId: string;
  };
}

describe('WebSocketGateway', () => {
  let gateway: WebSocketGateway;
  let mockServer: Server;
  let mockClient: AuthenticatedSocket;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [WebSocketGateway],
    }).compile();

    gateway = module.get<WebSocketGateway>(WebSocketGateway);

    // Mock Socket.IO server
    mockServer = {
      to: vi.fn().mockReturnThis(),
      emit: vi.fn(),
    } as unknown as Server;

    // Mock authenticated client
    mockClient = {
      id: 'test-socket-id',
      join: vi.fn(),
      leave: vi.fn(),
      emit: vi.fn(),
      data: {
        userId: 'user-123',
        workspaceId: 'workspace-456',
      },
      handshake: {
        auth: {
          token: 'valid-token',
        },
      },
    } as unknown as AuthenticatedSocket;

    gateway.server = mockServer;
  });

  describe('handleConnection', () => {
    it('should join client to workspace room on connection', async () => {
      await gateway.handleConnection(mockClient);

      expect(mockClient.join).toHaveBeenCalledWith('workspace:workspace-456');
    });

    it('should reject connection without authentication', async () => {
      const unauthClient = {
        ...mockClient,
        data: {},
        disconnect: vi.fn(),
      } as unknown as AuthenticatedSocket;

      await gateway.handleConnection(unauthClient);

      expect(unauthClient.disconnect).toHaveBeenCalled();
    });
  });

  describe('handleDisconnect', () => {
    it('should leave workspace room on disconnect', () => {
      gateway.handleDisconnect(mockClient);

      expect(mockClient.leave).toHaveBeenCalledWith('workspace:workspace-456');
    });
  });

  describe('emitTaskCreated', () => {
    it('should emit task:created event to workspace room', () => {
      const task = {
        id: 'task-1',
        title: 'Test Task',
        workspaceId: 'workspace-456',
      };

      gateway.emitTaskCreated('workspace-456', task);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('task:created', task);
    });
  });

  describe('emitTaskUpdated', () => {
    it('should emit task:updated event to workspace room', () => {
      const task = {
        id: 'task-1',
        title: 'Updated Task',
        workspaceId: 'workspace-456',
      };

      gateway.emitTaskUpdated('workspace-456', task);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('task:updated', task);
    });
  });

  describe('emitTaskDeleted', () => {
    it('should emit task:deleted event to workspace room', () => {
      const taskId = 'task-1';

      gateway.emitTaskDeleted('workspace-456', taskId);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('task:deleted', { id: taskId });
    });
  });

  describe('emitEventCreated', () => {
    it('should emit event:created event to workspace room', () => {
      const event = {
        id: 'event-1',
        title: 'Test Event',
        workspaceId: 'workspace-456',
      };

      gateway.emitEventCreated('workspace-456', event);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('event:created', event);
    });
  });

  describe('emitEventUpdated', () => {
    it('should emit event:updated event to workspace room', () => {
      const event = {
        id: 'event-1',
        title: 'Updated Event',
        workspaceId: 'workspace-456',
      };

      gateway.emitEventUpdated('workspace-456', event);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('event:updated', event);
    });
  });

  describe('emitEventDeleted', () => {
    it('should emit event:deleted event to workspace room', () => {
      const eventId = 'event-1';

      gateway.emitEventDeleted('workspace-456', eventId);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('event:deleted', { id: eventId });
    });
  });

  describe('emitProjectUpdated', () => {
    it('should emit project:updated event to workspace room', () => {
      const project = {
        id: 'project-1',
        name: 'Updated Project',
        workspaceId: 'workspace-456',
      };

      gateway.emitProjectUpdated('workspace-456', project);

      expect(mockServer.to).toHaveBeenCalledWith('workspace:workspace-456');
      expect(mockServer.emit).toHaveBeenCalledWith('project:updated', project);
    });
  });
});
