import { describe, it, expect, vi, beforeEach } from 'vitest';

describe('Socket Autodraft Events', () => {
  let mockSocket: any;
  let mockIO: any;

  beforeEach(() => {
    vi.clearAllMocks();

    mockSocket = {
      id: 'socket-123',
      join: vi.fn(),
      leave: vi.fn(),
      emit: vi.fn(),
      on: vi.fn(),
    };

    mockIO = {
      to: vi.fn().mockReturnThis(),
      emit: vi.fn(),
      on: vi.fn(),
    };
  });

  describe('Team Connection Tracking', () => {
    it('should emit team-connected event when team joins draft', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // Simulate joining draft with teamId
      mockSocket.join(`draft-${seasonId}`);
      mockSocket.join(`team-${teamId}`);
      mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });

      expect(mockSocket.join).toHaveBeenCalledWith(`draft-${seasonId}`);
      expect(mockSocket.join).toHaveBeenCalledWith(`team-${teamId}`);
      expect(mockIO.to).toHaveBeenCalledWith(`draft-${seasonId}`);
      expect(mockIO.emit).toHaveBeenCalledWith('team-connected', { teamId });
    });

    it('should emit team-disconnected event when team leaves draft', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // Simulate leaving draft
      mockSocket.leave(`draft-${seasonId}`);
      mockSocket.leave(`team-${teamId}`);
      mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });

      expect(mockSocket.leave).toHaveBeenCalledWith(`draft-${seasonId}`);
      expect(mockSocket.leave).toHaveBeenCalledWith(`team-${teamId}`);
      expect(mockIO.emit).toHaveBeenCalledWith('team-disconnected', { teamId });
    });

    it('should emit team-disconnected on socket disconnect', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // Simulate disconnect
      mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });

      expect(mockIO.emit).toHaveBeenCalledWith('team-disconnected', { teamId });
    });

    it('should not emit connection events when joining without teamId', () => {
      const seasonId = 'season-123';

      // Simulate joining draft without teamId (commissioner/spectator)
      mockSocket.join(`draft-${seasonId}`);

      expect(mockSocket.join).toHaveBeenCalledWith(`draft-${seasonId}`);
      expect(mockIO.emit).not.toHaveBeenCalledWith('team-connected', expect.anything());
    });
  });

  describe('Autodraft Status Updates', () => {
    it('should broadcast autodraft-updated event to draft room', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'next_pick',
      });

      expect(mockIO.to).toHaveBeenCalledWith(`draft-${seasonId}`);
      expect(mockIO.emit).toHaveBeenCalledWith('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'next_pick',
      });
    });

    it('should broadcast when autodraft is disabled', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId,
        isEnabled: false,
        mode: 'next_pick',
      });

      expect(mockIO.emit).toHaveBeenCalledWith('autodraft-updated', {
        teamId,
        isEnabled: false,
        mode: 'next_pick',
      });
    });

    it('should broadcast mode changes', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // Change from next_pick to while_on
      mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'while_on',
      });

      expect(mockIO.emit).toHaveBeenCalledWith('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'while_on',
      });
    });

    it('should include all required fields in autodraft-updated event', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'next_pick',
      });

      const emitCall = mockIO.emit.mock.calls[0];
      expect(emitCall[0]).toBe('autodraft-updated');
      expect(emitCall[1]).toHaveProperty('teamId');
      expect(emitCall[1]).toHaveProperty('isEnabled');
      expect(emitCall[1]).toHaveProperty('mode');
    });
  });

  describe('Event Ordering', () => {
    it('should emit team-connected before autodraft-updated on join', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      const emitSpy = vi.fn();
      mockIO.emit = emitSpy;

      // Join and set autodraft
      mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });
      mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'next_pick',
      });

      expect(emitSpy).toHaveBeenNthCalledWith(1, 'team-connected', { teamId });
      expect(emitSpy).toHaveBeenNthCalledWith(2, 'autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'next_pick',
      });
    });

    it('should emit autodraft-updated before team-disconnected on leave', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      const emitSpy = vi.fn();
      mockIO.emit = emitSpy;

      // Disable autodraft then leave
      mockIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId,
        isEnabled: false,
        mode: 'next_pick',
      });
      mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });

      expect(emitSpy).toHaveBeenNthCalledWith(1, 'autodraft-updated', {
        teamId,
        isEnabled: false,
        mode: 'next_pick',
      });
      expect(emitSpy).toHaveBeenNthCalledWith(2, 'team-disconnected', { teamId });
    });
  });

  describe('Room Isolation', () => {
    it('should only emit to specific draft room', () => {
      const seasonId1 = 'season-123';
      const seasonId2 = 'season-456';
      const teamId = 'team-789';

      const toSpy = vi.fn().mockReturnThis();
      mockIO.to = toSpy;

      mockIO.to(`draft-${seasonId1}`).emit('autodraft-updated', {
        teamId,
        isEnabled: true,
        mode: 'next_pick',
      });

      expect(toSpy).toHaveBeenCalledWith(`draft-${seasonId1}`);
      expect(toSpy).not.toHaveBeenCalledWith(`draft-${seasonId2}`);
    });

    it('should broadcast to all clients in the room', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // Multiple clients in same room should all receive the event
      mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });

      expect(mockIO.to).toHaveBeenCalledWith(`draft-${seasonId}`);
      expect(mockIO.emit).toHaveBeenCalledWith('team-connected', { teamId });
    });
  });

  describe('Error Handling', () => {
    it('should handle missing seasonId gracefully', () => {
      const seasonId = '';
      
      if (!seasonId) {
        // Should not attempt to join or emit
        expect(mockSocket.join).not.toHaveBeenCalled();
        expect(mockIO.emit).not.toHaveBeenCalled();
      }
    });

    it('should handle missing teamId gracefully', () => {
      const seasonId = 'season-123';
      const teamId = undefined;

      mockSocket.join(`draft-${seasonId}`);

      // Should join draft room but not emit team-connected
      expect(mockSocket.join).toHaveBeenCalledWith(`draft-${seasonId}`);
      
      if (!teamId) {
        expect(mockIO.emit).not.toHaveBeenCalledWith('team-connected', expect.anything());
      }
    });
  });

  describe('Multiple Connections', () => {
    it('should handle same team connecting from multiple devices', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // First connection
      mockSocket.join(`draft-${seasonId}`);
      mockSocket.join(`team-${teamId}`);
      mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });

      // Second connection (same team)
      const mockSocket2 = { ...mockSocket, id: 'socket-456' };
      mockSocket2.join(`draft-${seasonId}`);
      mockSocket2.join(`team-${teamId}`);
      mockIO.to(`draft-${seasonId}`).emit('team-connected', { teamId });

      // Both should emit connection events
      expect(mockIO.emit).toHaveBeenCalledTimes(2);
    });

    it('should only disconnect when all connections are closed', () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      // Two connections
      const socket1 = { ...mockSocket, id: 'socket-1' };
      const socket2 = { ...mockSocket, id: 'socket-2' };

      // First disconnects
      socket1.leave(`draft-${seasonId}`);
      // Should not emit team-disconnected yet

      // Second disconnects
      socket2.leave(`draft-${seasonId}`);
      mockIO.to(`draft-${seasonId}`).emit('team-disconnected', { teamId });

      // In practice, this would be handled by tracking connection count
      expect(mockSocket.leave).toHaveBeenCalled();
    });
  });
});
