/* eslint-disable no-magic-numbers */

import { setUpBoard, impure } from './boardSetup.js';

expect.extend({
  toEqualAsASet(received, collection) {
    const actual = new Set([...received]);
    const expected = new Set([...collection]);
    const missing = [...expected].filter((element) => !actual.has(element)).sort();
    const extra = [...actual].filter((element) => !expected.has(element)).sort();
    const pass = missing.length === 0 && extra.length === 0;
    let message =
        `- ${pass ? 'Unexpected' : 'Expected'}  - ${missing.length}\n` +
        `+ ${pass ? 'Received  ' : 'Received'}  + ${extra.length}\n\n`;
    for (const missingElement of missing) {
      message += `- ${missingElement.toString().replace(/\n/g, '\n- ')}\n\n`;
    }
    for (const extraElement of extra) {
      message += `+ ${extraElement.toString().replace(/\n/g, '\n+ ')}\n\n`;
    }
    return {
      message: () => message,
      pass,
    };
  },
});

function recordPlacements(boardWidth, boardHeight, occupied, requestedDragonCount) {
  const game = {
    boardWidth,
    boardHeight,
    dragon: 'd',
  };
  const position = {
    getColorAndPieceType: jest.fn().mockName('getColorAndPieceType').mockImplementation(
      (x, y) => [undefined, occupied.has(`${x},${y}`) ? 'occupied' : undefined],
    ),
    modified: jest.fn().mockName('modified'),
  };
  position.modified.mockReturnValue(position);
  impure.getRandomIndex = jest.fn().mockName('getRandomIndex').mockReturnValue(0);
  const result = setUpBoard(game, position, requestedDragonCount);
  expect(result).toBe(position);
  return new Set(position.modified.mock.calls.map(([x, y]) => `${x},${y}`));
}

describe('setUpBoard', () => {
  test('does nothing when no dragons are requested', () => {
    expect(recordPlacements(1, 3, new Set(), 0)).toEqualAsASet([]);
  });
  test('does nothing when a dragon is requested, but the board has no center', () => {
    expect(recordPlacements(2, 3, new Set(), 1)).toEqualAsASet([]);
  });
  test('adds a dragon in the center when one is requested on a board with a center', () => {
    expect(recordPlacements(1, 3, new Set(), 1)).toEqualAsASet([
      '0,1',
    ]);
  });
  test('rounds down when an odd number of dragons are requested, but the board has no center', () => {
    expect(recordPlacements(2, 3, new Set(), 3)).toEqualAsASet([
      '0,0',
      '1,2',
    ]);
  });
  test('adds a dragon in the center when an odd number are requested on a board with a center', () => {
    expect(recordPlacements(3, 3, new Set(), 3)).toEqualAsASet([
      '1,1',
      '0,0',
      '2,2',
    ]);
  });
  test('does not exceed a 75% density', () => {
    expect(recordPlacements(1, 8, new Set(), 8)).toEqualAsASet([
      '0,0',
      '0,7',
      '0,3',
      '0,4',
      '0,2',
      '0,5',
    ]);
  });
  test('does not place dragons on fully occupied non-center points', () => {
    expect(recordPlacements(3, 3, new Set([
      '0,0',
      '2,2',
    ]), 3)).toEqualAsASet([
      '1,1',
      '1,0',
      '1,2',
    ]);
  });
  test('does not place dragons on partially occupied non-center points', () => {
    expect(recordPlacements(3, 3, new Set([
      '0,0',
    ]), 3)).toEqualAsASet([
      '1,1',
      '1,0',
      '1,2',
    ]);
  });
});