import React from 'react';
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import DeathwatchRoller from '../components/DeathwatchRoller';

jest.setTimeout(30000);

const mockPlayers = [
  {
    name: 'Alpha',
    tabInfo: {
      characteristics: { ag: 40, bs: 60, ws: 45, t: 40 },
      wounds: 18,
      weapons: [{ name: 'Bolter', class: 'basic', damage: '2d10+5', pen: 5, rof: 4, modes: ['single', 'semi', 'full'] }],
    },
  },
  {
    name: 'Bravo',
    tabInfo: {
      characteristics: { ag: 38, bs: 58, ws: 44, t: 42 },
      wounds: 17,
      weapons: [{ name: 'Bolt Pistol', class: 'pistol', damage: '2d10+5', pen: 5, rof: 3, modes: ['single', 'semi'] }],
    },
  },
  {
    name: 'Charlie',
    tabInfo: {
      characteristics: { ag: 36, bs: 55, ws: 43, t: 41 },
      wounds: 16,
      weapons: [{ name: 'Plasma Gun', class: 'basic', damage: '1d10+9', pen: 8, rof: 2, modes: ['single', 'semi'] }],
    },
  },
  {
    name: 'Delta',
    tabInfo: {
      characteristics: { ag: 34, bs: 52, ws: 42, t: 40 },
      wounds: 15,
      weapons: [{ name: 'Chainsword', class: 'melee', damage: '1d10+3', pen: 4, rof: 1, modes: ['single'] }],
    },
  },
];

const mockHordes = [
  { name: 'Mutant Horde', horde: true, magnitude: 20, wounds: 20, tb: 4, ag: 20, ws: 30, bs: 25, damage: '1d10+3' },
  { name: 'PDF Guardsman Horde', horde: true, magnitude: 24, wounds: 24, tb: 3, ag: 25, ws: 32, bs: 30, damage: '1d10+2' },
  { name: 'Rebel Horde', horde: true, magnitude: 22, wounds: 22, tb: 3, ag: 25, ws: 30, bs: 30, damage: '1d10+2' },
  { name: 'Kroot Carnivore Horde', horde: true, magnitude: 18, wounds: 18, tb: 4, ag: 35, ws: 38, bs: 30, damage: '1d10+4' },
  { name: 'Termagaunt Horde', horde: true, magnitude: 26, wounds: 26, tb: 3, ag: 30, ws: 35, bs: 30, damage: '1d10+3' },
];

function mockFetch(url) {
  if (url === '/api/players') {
    return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockPlayers) });
  }
  if (url === '/api/bestiary/enemies') {
    return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(mockHordes) });
  }
  if (url === '/api/missions/active/player') {
    return Promise.resolve({ ok: false, status: 404, json: () => Promise.resolve({}) });
  }
  if (url === '/api/weapons') {
    return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve([]) });
  }
  if (typeof url === 'string' && url.startsWith('/api/players/')) {
    const name = decodeURIComponent(url.split('/').pop() || '');
    const player = mockPlayers.find(entry => entry.name === name);
    return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(player || { name }) });
  }
  return Promise.resolve({ ok: false, status: 404, json: () => Promise.resolve({}) });
}

function makeRandomSequence() {
  const seq = [];
  // startCombat: 4 player init dice + 1 enemy init die
  seq.push(0.89, 0.69, 0.49, 0.29, 0.09);
  // Roll 1: Alpha attacks (d100 + 2d10 + uid)
  seq.push(0.30, 0.50, 0.50, 0.50);
  // Roll 2: Bravo attacks (d100 + 2d10 + uid)
  seq.push(0.40, 0.50, 0.50, 0.50);
  // Roll 3: Charlie attacks (d100 + 1d10 + uid)
  seq.push(0.50, 0.50, 0.50);
  // Roll 4: Delta attacks (d100 + 1d10 + uid)
  seq.push(0.40, 0.50, 0.50);
  // Roll 5: Horde attacks (d100 reaction + 1d10 damage + uid)
  seq.push(0.60, 0.50, 0.50);
  // Pad
  for (let i = seq.length; i < 100; i++) seq.push(0.50);
  return seq;
}

describe('DeathwatchRoller horde combat', () => {
  beforeEach(() => {
    localStorage.clear();
    fetch.mockImplementation(mockFetch);
    jest.useFakeTimers();
  });

  afterEach(() => {
    cleanup();
    jest.useRealTimers();
    jest.restoreAllMocks();
  });

  test('runs 4 players against 5 horde setups with working roll progression', async () => {
    for (const horde of mockHordes) {
      const randomValues = makeRandomSequence();
      jest.spyOn(Math, 'random').mockImplementation(() => (randomValues.length ? randomValues.shift() : 0.5));

      const { unmount } = render(<DeathwatchRoller authedPlayer="gm" />);

      fireEvent.click(await screen.findByRole('button', { name: 'Horde Combat' }));

      const getHordeEnemySelect = () => screen.getByText('Horde Enemy').parentElement.querySelector('select');
      await waitFor(() => {
        const select = getHordeEnemySelect();
        expect(select).not.toBeNull();
        expect(Array.from(select.querySelectorAll('option')).some(option => option.value === horde.name)).toBe(true);
      });
      fireEvent.change(getHordeEnemySelect(), { target: { value: horde.name } });
      await waitFor(() => expect(getHordeEnemySelect().value).toBe(horde.name));
      fireEvent.click(screen.getByRole('button', { name: '4' }));
      await waitFor(() => expect(screen.getByText('4 player(s) selected')).toBeInTheDocument());
      await waitFor(() => expect(screen.getByRole('button', { name: 'Start Combat' })).not.toBeDisabled());
      fireEvent.click(screen.getByRole('button', { name: 'Start Combat' }));

      await waitFor(() => expect(screen.getByText(`Horde Combat: ${horde.name}`)).toBeInTheDocument());
      expect(screen.getAllByText(/Round 1/).length).toBeGreaterThan(0);

      // Roll 1: Alpha attacks (highest initiative), then advances to Bravo
      await act(async () => {
        fireEvent.click(screen.getByRole('button', { name: 'Roll' }));
        jest.runAllTimers();
      });
      await waitFor(() => expect(screen.getAllByText(/Bravo's turn/i).length).toBeGreaterThan(0));

      // Roll 2: Bravo attacks, then advances to Charlie
      await act(async () => {
        fireEvent.click(screen.getByRole('button', { name: 'Roll' }));
        jest.runAllTimers();
      });
      await waitFor(() => expect(screen.getAllByText(/Charlie's turn/i).length).toBeGreaterThan(0));

      // Roll 3: Charlie attacks, then advances to Delta
      await act(async () => {
        fireEvent.click(screen.getByRole('button', { name: 'Roll' }));
        jest.runAllTimers();
      });
      await waitFor(() => expect(screen.getAllByText(/Delta's turn/i).length).toBeGreaterThan(0));

      // Roll 4: Delta attacks, then advances to horde's turn
      await act(async () => {
        fireEvent.click(screen.getByRole('button', { name: 'Roll' }));
        jest.runAllTimers();
      });
      await waitFor(() => expect(screen.getAllByText(new RegExp(`${horde.name}'s turn`, 'i')).length).toBeGreaterThan(0));

      // Roll 5: Horde attacks, then advances back to Alpha (Round 2)
      await act(async () => {
        fireEvent.click(screen.getByRole('button', { name: 'Roll' }));
        jest.runAllTimers();
      });
      await waitFor(() => expect(screen.getAllByText(/Alpha's turn/i).length).toBeGreaterThan(0));

      // Verify the roll history has accumulated entries from the combat
      await waitFor(() => {
        expect(screen.getAllByText(/Bolter|Bolt Pistol|Plasma Gun|Chainsword/i).length).toBeGreaterThan(0);
      });

      unmount();
      jest.restoreAllMocks();
      fetch.mockImplementation(mockFetch);
    }
  });
});