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

// Mock dependencies before imports
vi.mock('drizzle-orm/postgres-js', () => ({
  drizzle: vi.fn(() => mockDb),
}));

vi.mock('postgres', () => ({
  default: vi.fn(() => ({})),
}));

vi.mock('../socket', () => ({
  getSocketIO: vi.fn(() => mockSocketIO),
}));

let mockDb: any;
let mockSocketIO: any;

// Setup mocks
beforeEach(() => {
  mockSocketIO = {
    to: vi.fn().mockReturnThis(),
    emit: vi.fn(),
  };

  mockDb = {
    query: {
      seasons: { findMany: vi.fn(), findFirst: vi.fn() },
      draftSlots: { findMany: vi.fn() },
      draftTimers: { findFirst: vi.fn() },
      autodraftSettings: { findFirst: vi.fn() },
      draftPicks: { findMany: vi.fn(), findFirst: vi.fn() },
      draftQueue: { findMany: vi.fn() },
      participants: { findMany: vi.fn() },
      seasonTemplateSports: { findMany: vi.fn() },
    },
    update: vi.fn().mockReturnThis(),
    set: vi.fn().mockReturnThis(),
    where: vi.fn().mockReturnThis(),
    returning: vi.fn(),
    insert: vi.fn().mockReturnThis(),
    values: vi.fn().mockReturnThis(),
    delete: vi.fn().mockReturnThis(),
    select: vi.fn().mockReturnThis(),
    from: vi.fn().mockReturnThis(),
    innerJoin: vi.fn().mockReturnThis(),
    orderBy: vi.fn().mockReturnThis(),
    limit: vi.fn().mockReturnThis(),
  };
});

describe('Timer Autodraft Integration', () => {
  describe('Autodraft Settings Check', () => {
    it('should check autodraft settings when timer expires', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';

      mockDb.query.seasons.findMany.mockResolvedValue([{
        id: seasonId, status: 'draft', draftPaused: false,
        currentPickNumber: 1, draftInitialTime: 120, draftIncrementTime: 30, draftRounds: 10,
      }]);
      mockDb.query.draftSlots.findMany.mockResolvedValue([
        { id: 'slot-1', teamId, draftOrder: 1 },
        { id: 'slot-2', teamId: 'team-2', draftOrder: 2 },
      ]);
      mockDb.query.draftTimers.findFirst.mockResolvedValue({ id: 'timer-1', seasonId, teamId, timeRemaining: 0 });
      mockDb.query.autodraftSettings.findFirst.mockResolvedValue({
        id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', queueOnly: false,
      });

      expect(mockDb.query.autodraftSettings.findFirst).toBeDefined();
    });

    it('should use regular auto-pick when autodraft is disabled', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      mockDb.query.autodraftSettings.findFirst.mockResolvedValue({
        id: 'settings-1', seasonId, teamId, isEnabled: false, mode: 'next_pick', queueOnly: false,
      });
      const settings = await mockDb.query.autodraftSettings.findFirst();
      expect(settings.isEnabled).toBe(false);
    });

    it('should handle missing autodraft settings', async () => {
      mockDb.query.autodraftSettings.findFirst.mockResolvedValue(null);
      const settings = await mockDb.query.autodraftSettings.findFirst();
      expect(settings).toBeNull();
    });
  });

  describe('Autodraft Mode Handling', () => {
    it('should disable autodraft after pick when mode is next_pick', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', queueOnly: false };
      mockDb.returning.mockResolvedValue([{ ...autodraftSettings, isEnabled: false, updatedAt: new Date() }]);

      if (autodraftSettings.isEnabled && autodraftSettings.mode === 'next_pick') {
        await mockDb.update();
        await mockDb.set({ isEnabled: false, updatedAt: new Date() });
        await mockDb.where();
        const result = await mockDb.returning();
        expect(result[0].isEnabled).toBe(false);
      }
    });

    it('should keep autodraft enabled when mode is while_on (All Picks)', async () => {
      const autodraftSettings = { isEnabled: true, mode: 'while_on', queueOnly: false };
      if (autodraftSettings.mode === 'while_on') {
        expect(autodraftSettings.isEnabled).toBe(true);
      }
    });
  });

  describe('Autodraft Pick Logic', () => {
    it('should pick from queue first when autodraft is enabled', async () => {
      mockDb.query.draftQueue.findMany.mockResolvedValue([
        { id: 'queue-1', participantId: 'participant-1', queuePosition: 1 },
        { id: 'queue-2', participantId: 'participant-2', queuePosition: 2 },
      ]);
      mockDb.query.draftPicks.findMany.mockResolvedValue([]);

      const queueItems = await mockDb.query.draftQueue.findMany();
      const draftedPicks = await mockDb.query.draftPicks.findMany();
      const draftedParticipantIds = draftedPicks.map((p: any) => p.participantId);
      const availableQueueItem = queueItems.find((item: any) => !draftedParticipantIds.includes(item.participantId));

      expect(availableQueueItem).toBeDefined();
      expect(availableQueueItem.participantId).toBe('participant-1');
    });

    it('should skip drafted participants in queue and try next', async () => {
      mockDb.query.draftQueue.findMany.mockResolvedValue([
        { id: 'queue-1', participantId: 'participant-1', queuePosition: 1 },
        { id: 'queue-2', participantId: 'participant-2', queuePosition: 2 },
      ]);
      mockDb.query.draftPicks.findMany.mockResolvedValue([{ id: 'pick-1', participantId: 'participant-1' }]);

      const queueItems = await mockDb.query.draftQueue.findMany();
      const draftedPicks = await mockDb.query.draftPicks.findMany();
      const draftedParticipantIds = draftedPicks.map((p: any) => p.participantId);
      const availableQueueItem = queueItems.find((item: any) => !draftedParticipantIds.includes(item.participantId));

      expect(availableQueueItem.participantId).toBe('participant-2');
    });

    it('should fall back to highest EV when queue is empty and queueOnly is OFF', async () => {
      mockDb.query.draftQueue.findMany.mockResolvedValue([]);
      mockDb.query.participants.findMany.mockResolvedValue([
        { id: 'participant-high-ev', name: 'High EV Participant', expectedValue: 1000 },
      ]);

      const queueItems = await mockDb.query.draftQueue.findMany();
      expect(queueItems.length).toBe(0);

      const queueOnly = false;
      const availableParticipants = await mockDb.query.participants.findMany();
      const selectedParticipant = queueOnly ? null : availableParticipants[0];
      expect(selectedParticipant).not.toBeNull();
      expect(selectedParticipant?.expectedValue).toBe(1000);
    });
  });

  describe('Queue-Only Constraint (AC2 & AC3)', () => {
    it('should NOT fall back to highest EV when queueOnly is ON and queue is empty', async () => {
      mockDb.query.draftQueue.findMany.mockResolvedValue([]);
      const queueItems = await mockDb.query.draftQueue.findMany();
      expect(queueItems.length).toBe(0);

      const queueOnly = true;
      const selectedParticipant = queueOnly ? null : { id: 'participant-high-ev' };
      expect(selectedParticipant).toBeNull();
    });

    it('should disable autodraft when queueOnly is ON and queue becomes empty (AC3)', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', queueOnly: true };

      mockDb.returning.mockResolvedValue([{ ...autodraftSettings, isEnabled: false, updatedAt: new Date() }]);

      const participantId = null; // empty queue + queueOnly
      if (!participantId && autodraftSettings.queueOnly) {
        await mockDb.update();
        await mockDb.set({ isEnabled: false, updatedAt: new Date() });
        await mockDb.where();
        const result = await mockDb.returning();
        expect(result[0].isEnabled).toBe(false);

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

    it('should skip drafted queue items and continue to next available item', async () => {
      const queue = [
        { id: 'queue-1', participantId: 'participant-1', queuePosition: 1 },
        { id: 'queue-2', participantId: 'participant-2', queuePosition: 2 },
      ];
      const drafted = new Set(['participant-1']);
      const availableQueueItem = queue.find((item) => !drafted.has(item.participantId));
      expect(availableQueueItem?.participantId).toBe('participant-2');
    });

    it('should auto-disable when all queue items are drafted and queueOnly is ON', async () => {
      const queue = [
        { id: 'queue-1', participantId: 'participant-1', queuePosition: 1 },
        { id: 'queue-2', participantId: 'participant-2', queuePosition: 2 },
      ];
      const drafted = new Set(['participant-1', 'participant-2']);
      const queueOnly = true;
      const availableQueueItem = queue.find((item) => !drafted.has(item.participantId));

      expect(availableQueueItem).toBeUndefined();
      const selectedParticipant = queueOnly ? null : { id: 'fallback-ev' };
      expect(selectedParticipant).toBeNull();
    });
  });

  describe('Three-State Autodraft (AC1)', () => {
    it('should map Off state to isEnabled=false', () => {
      const offState = { isEnabled: false, mode: 'next_pick', queueOnly: false };
      expect(offState.isEnabled).toBe(false);
    });

    it('should map Next Pick state to isEnabled=true, mode=next_pick', () => {
      const nextPickState = { isEnabled: true, mode: 'next_pick', queueOnly: false };
      expect(nextPickState.isEnabled).toBe(true);
      expect(nextPickState.mode).toBe('next_pick');
    });

    it('should map All Picks state to isEnabled=true, mode=while_on', () => {
      const allPicksState = { isEnabled: true, mode: 'while_on', queueOnly: false };
      expect(allPicksState.isEnabled).toBe(true);
      expect(allPicksState.mode).toBe('while_on');
    });

    it('should transition Off → Next Pick correctly', () => {
      const initial = { isEnabled: false, mode: 'next_pick' };
      const updated = { isEnabled: true, mode: 'next_pick' };
      expect(updated.isEnabled).not.toBe(initial.isEnabled);
      expect(updated.mode).toBe('next_pick');
    });

    it('should transition Next Pick → All Picks correctly', () => {
      const nextPick = { isEnabled: true, mode: 'next_pick' };
      const allPicks = { ...nextPick, mode: 'while_on' };
      expect(allPicks.mode).toBe('while_on');
      expect(allPicks.isEnabled).toBe(true);
    });

    it('should auto-disable if queue is empty and queueOnly is ON (AC3)', () => {
      const settings = { isEnabled: true, mode: 'next_pick', queueOnly: true };
      const queue: any[] = [];
      const shouldAutoDisable = settings.queueOnly && queue.length === 0;
      const finalSettings = shouldAutoDisable ? { ...settings, isEnabled: false } : settings;
      expect(finalSettings.isEnabled).toBe(false);
    });

    it('should NOT auto-disable if queue is empty and queueOnly is OFF', () => {
      const settings = { isEnabled: true, mode: 'next_pick', queueOnly: false };
      const queue: any[] = [];
      const shouldAutoDisable = settings.queueOnly && queue.length === 0;
      const finalSettings = shouldAutoDisable ? { ...settings, isEnabled: false } : settings;
      expect(finalSettings.isEnabled).toBe(true);
    });
  });

  describe('Socket Event Emission', () => {
    it('should emit autodraft-updated with queueOnly when disabling next_pick mode', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      mockSocketIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId, isEnabled: false, mode: 'next_pick', queueOnly: false,
      });
      expect(mockSocketIO.emit).toHaveBeenCalledWith('autodraft-updated', {
        teamId, isEnabled: false, mode: 'next_pick', queueOnly: false,
      });
    });

    it('should emit pick-made event after autodraft pick', async () => {
      const seasonId = 'season-123';
      const pickData = { pick: { id: 'pick-1', participantId: 'participant-1', pickNumber: 1 }, nextPickNumber: 2, isDraftComplete: false };
      mockSocketIO.to(`draft-${seasonId}`).emit('pick-made', pickData);
      expect(mockSocketIO.emit).toHaveBeenCalledWith('pick-made', pickData);
    });

    it('should emit autodraft-updated with queueOnly preserved on auto-disable (AC3)', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      mockSocketIO.to(`draft-${seasonId}`).emit('autodraft-updated', {
        teamId, isEnabled: false, mode: 'next_pick', queueOnly: true,
      });
      expect(mockSocketIO.emit).toHaveBeenCalledWith('autodraft-updated', {
        teamId, isEnabled: false, mode: 'next_pick', queueOnly: true,
      });
    });
  });

  describe('Back-to-Back Picks', () => {
    it('should only autodraft first pick when mode is next_pick', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      const autodraftSettings = { id: 'settings-1', seasonId, teamId, isEnabled: true, mode: 'next_pick', queueOnly: false };

      if (autodraftSettings.isEnabled && autodraftSettings.mode === 'next_pick') {
        const updatedSettings = { ...autodraftSettings, isEnabled: false };
        expect(updatedSettings.isEnabled).toBe(false);
      }

      const settingsForSecondPick = { ...autodraftSettings, isEnabled: false };
      expect(settingsForSecondPick.isEnabled).toBe(false);
    });

    it('should continue autodrafting consecutive picks when mode is while_on (All Picks)', async () => {
      const autodraftSettings = { isEnabled: true, mode: 'while_on', queueOnly: false };
      if (autodraftSettings.mode === 'while_on') expect(autodraftSettings.isEnabled).toBe(true);
      expect(autodraftSettings.isEnabled).toBe(true);
    });
  });

  describe('Time Increment', () => {
    it('should add increment time after autodraft pick', () => {
      expect(0 + 30).toBe(30);
    });

    it('should emit timer-update after adding increment', async () => {
      const seasonId = 'season-123';
      const teamId = 'team-456';
      mockSocketIO.to(`draft-${seasonId}`).emit('timer-update', {
        seasonId, teamId, timeRemaining: 30, currentPickNumber: 1,
      });
      expect(mockSocketIO.emit).toHaveBeenCalledWith('timer-update', {
        seasonId, teamId, timeRemaining: 30, currentPickNumber: 1,
      });
    });
  });
});
