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

jest.mock('axios');

const gmMission = {
  id: 7,
  name: 'Landing Goes Wrong',
  current_scene: 0,
  scenes: [
    {
      title: 'Dock Breach',
      type: 'combat',
      description: 'Boarding torpedoes punch into a void dock while cultists hold the gantries.',
      enemies: [
        { name: 'Cultist Cell', wounds: 8 },
        { name: 'Heavy Stubber Team', wounds: 10 },
      ],
      combatState: {
        initiatives: [
          { player: 'Chris', total: 12, type: 'player' },
          { player: 'Rafen', total: 10, type: 'player' },
          { name: 'Cultist Cell', total: 9, type: 'npc', ts: 1 },
        ],
        turnKey: 'player:chris',
      },
    },
  ],
};

const gmMissionWithPartialLayout = {
  ...gmMission,
  scenes: [
    {
      ...gmMission.scenes[0],
      battlemapState: {
        layout: {
          gridWidth: 40,
          gridHeight: 30,
          zones: [{ id: 'extraction', name: 'Extraction', x: 1, y: 11, w: 8, h: 8, tone: 'spawn' }],
        },
      },
    },
  ],
};

describe('BattlemapTab', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    jest.spyOn(global, 'setInterval').mockImplementation(() => 1);
    jest.spyOn(global, 'clearInterval').mockImplementation(() => {});
  });

  afterEach(() => {
    global.setInterval.mockRestore();
    global.clearInterval.mockRestore();
  });

  test('loads the gene-vault mission board, shows objectives, and persists GM token moves', async () => {
    axios.get.mockImplementation((url) => {
      if (url === '/api/players') {
        return Promise.resolve({
          data: [
            { name: 'Chris', tabInfo: { movement: { half: 3, full: 6, charge: 6 } } },
            { name: 'Rafen', tabInfo: { movement: { half: 3, full: 6, charge: 6 } } },
          ],
        });
      }
      return Promise.resolve({ data: gmMission });
    });
    axios.put.mockResolvedValue({ data: { success: true } });

    const { unmount } = render(<BattlemapTab authedPlayer="gm" sessionId="session-test" />);

    expect(await screen.findByText('Landing Zone Breach')).toBeInTheDocument();
    expect(await screen.findByText('Landing Goes Wrong')).toBeInTheDocument();
    expect(screen.getByText(/Dock Breach/)).toBeInTheDocument();
    expect(screen.getByText(/1 checker = 10 metres/i)).toBeInTheDocument();
    expect(screen.getByText(/Activate 2 of 3 stabilization stations/i)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Sync Combatants' })).toBeEnabled();

    await waitFor(() => {
      expect(screen.getByText(/Turn Chris/)).toBeInTheDocument();
      expect(screen.getByText('North maglift')).toBeInTheDocument();
      expect(screen.getByTestId('battlemap-token-player:chris')).toBeInTheDocument();
      expect(screen.getByTestId('battlemap-token-enemy:cultist cell:1')).toBeInTheDocument();
    });

    fireEvent.click(screen.getByTestId('battlemap-token-player:chris'));
    fireEvent.click(screen.getByRole('button', { name: 'Right' }));

    await waitFor(() => expect(axios.put).toHaveBeenCalled());
    unmount();
  });

  test('does not crash on older partial battlemap layout data', async () => {
    axios.get.mockImplementation((url) => {
      if (url === '/api/players') {
        return Promise.resolve({ data: [{ name: 'Chris', tabInfo: { movement: { half: 3, full: 6 } } }] });
      }
      return Promise.resolve({ data: gmMissionWithPartialLayout });
    });
    axios.put.mockResolvedValue({ data: { success: true } });

    const { unmount } = render(<BattlemapTab authedPlayer="gm" sessionId="session-test" />);
    expect(await screen.findByText('Landing Zone Breach')).toBeInTheDocument();
    expect(screen.getByText(/Cover Materials/i)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Sync Combatants' })).toBeEnabled();
    unmount();
  });

  test('tracks checker-by-checker movement across the turn budget', async () => {
    axios.get.mockImplementation((url) => {
      if (url === '/api/players') {
        return Promise.resolve({
          data: [
            { name: 'Chris', tabInfo: { movement: { half: 10, full: 20, charge: 20, run: 30 } } },
            { name: 'Rafen', tabInfo: { movement: { half: 10, full: 20, charge: 20, run: 30 } } },
          ],
        });
      }
      return Promise.resolve({ data: gmMission });
    });
    axios.put.mockResolvedValue({ data: { success: true } });

    const { unmount } = render(<BattlemapTab authedPlayer="gm" sessionId="session-test" />);

    await waitFor(() => expect(screen.getByTestId('battlemap-token-player:chris')).toBeInTheDocument());

    fireEvent.click(screen.getByTestId('battlemap-token-player:chris'));
    fireEvent.click(screen.getByRole('button', { name: /full/i }));

    const canvas = screen.getByTestId('battlemap-canvas');
    canvas.getBoundingClientRect = () => ({
      left: 0,
      top: 0,
      width: 400,
      height: 300,
      right: 400,
      bottom: 300,
    });

    const firstCircle = screen.getByTestId('battlemap-token-player:chris').querySelector('circle');
    const firstX = Number(firstCircle.getAttribute('cx'));
    const firstY = Number(firstCircle.getAttribute('cy'));
    fireEvent.click(canvas, {
      clientX: ((firstX + 1) / 40) * 400,
      clientY: (firstY / 30) * 300,
    });

    await waitFor(() => expect(screen.getByText(/Current turn: full movement, 1 checker\(s\) left\./i)).toBeInTheDocument());

    const secondCircle = screen.getByTestId('battlemap-token-player:chris').querySelector('circle');
    const secondX = Number(secondCircle.getAttribute('cx'));
    const secondY = Number(secondCircle.getAttribute('cy'));
    fireEvent.click(canvas, {
      clientX: ((secondX + 1) / 40) * 400,
      clientY: (secondY / 30) * 300,
    });

    await waitFor(() => {
      expect(axios.put).toHaveBeenCalledTimes(2);
      const payload = axios.put.mock.calls[1][1];
      expect(payload.patch.battlemapState.turnState.movementUsedBy['player:chris']).toBe(2);
      expect(payload.patch.battlemapState.turnState.moveModeBy['player:chris']).toBe('full');
    });

    unmount();
  });
});
