import { test, expect, type Page } from '@playwright/test';

interface GameHook {
  phase: string;
  hp: number;
  ammo: number;
  enemies: number;
  hasKey: boolean;
  x: number;
  y: number;
  angle: number;
  teleport: (x: number, y: number, angle?: number) => void;
}

declare global {
  interface Window {
    __game: GameHook;
  }
}

function collectErrors(page: Page): string[] {
  const errors: string[] = [];
  page.on('pageerror', (e) => errors.push(`pageerror: ${e.message}`));
  page.on('console', (m) => {
    if (m.type() === 'error') errors.push(`console: ${m.text()}`);
  });
  return errors;
}

async function startGame(page: Page): Promise<void> {
  await page.goto('/');
  await page.waitForFunction(() => window.__game !== undefined);
  await expect(page.locator('#ov-title')).toHaveText('HULLROT');
  await page.tap('#ov-btn');
  await page.waitForFunction(() => window.__game.phase === 'playing');
}

test('title screen shows and game starts', async ({ page }) => {
  const errors = collectErrors(page);
  await startGame(page);
  expect(await page.evaluate(() => window.__game.hp)).toBe(100);
  expect(errors).toEqual([]);
});

test('renderer draws a non-black scene', async ({ page }) => {
  const errors = collectErrors(page);
  await startGame(page);
  await page.waitForTimeout(500);
  const litRatio = await page.evaluate(() => {
    const c = document.getElementById('view') as HTMLCanvasElement;
    const ctx = c.getContext('2d')!;
    const d = ctx.getImageData(0, 0, c.width, c.height).data;
    let lit = 0;
    let total = 0;
    for (let i = 0; i < d.length; i += 4 * 97) {
      total++;
      if ((d[i] ?? 0) + (d[i + 1] ?? 0) + (d[i + 2] ?? 0) > 45) lit++;
    }
    return lit / total;
  });
  expect(litRatio).toBeGreaterThan(0.15);
  expect(errors).toEqual([]);
});

test('player can move forward', async ({ page }) => {
  const errors = collectErrors(page);
  await startGame(page);
  const before = await page.evaluate(() => ({ x: window.__game.x, y: window.__game.y }));
  await page.keyboard.down('w');
  await page.waitForTimeout(700);
  await page.keyboard.up('w');
  const after = await page.evaluate(() => ({ x: window.__game.x, y: window.__game.y }));
  const dist = Math.hypot(after.x - before.x, after.y - before.y);
  expect(dist).toBeGreaterThan(0.5);
  expect(errors).toEqual([]);
});

test('shooting kills an enemy and uses ammo', async ({ page }) => {
  const errors = collectErrors(page);
  await startGame(page);
  // face the antechamber husk point-blank
  await page.evaluate(() => window.__game.teleport(11.5, 7.0, Math.PI / 2));
  const enemies0 = await page.evaluate(() => window.__game.enemies);
  const ammo0 = await page.evaluate(() => window.__game.ammo);
  for (let i = 0; i < 6; i++) {
    await page.keyboard.press('Space');
    await page.waitForTimeout(480);
  }
  const enemies1 = await page.evaluate(() => window.__game.enemies);
  const ammo1 = await page.evaluate(() => window.__game.ammo);
  expect(enemies1).toBeLessThan(enemies0);
  expect(ammo1).toBeLessThan(ammo0);
  expect(errors).toEqual([]);
});

test('keycard pickup unlocks the hold and exit wins', async ({ page }) => {
  const errors = collectErrors(page);
  await startGame(page);
  // grab the keycard in the west storage room
  await page.evaluate(() => window.__game.teleport(2.5, 13.5));
  await page.waitForFunction(() => window.__game.hasKey);
  // approach the locked door from the antechamber, let it open, walk through
  await page.evaluate(() => window.__game.teleport(11.5, 6.6, -Math.PI / 2));
  await page.waitForTimeout(1200);
  await page.keyboard.down('w');
  await page.waitForTimeout(900);
  await page.keyboard.up('w');
  const y = await page.evaluate(() => window.__game.y);
  expect(y).toBeLessThan(5.5); // made it into the hold through the unlocked door
  // step onto the exit beacon
  await page.evaluate(() => window.__game.teleport(21.5, 1.6));
  await page.waitForFunction(() => window.__game.phase === 'won');
  expect(errors).toEqual([]);
});
