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

// The window.__game hook exposed by src/main.ts
interface GameApi {
  phase: string;
  hearts: number;
  gems: number;
  slimes: number;
  grassCut: number;
  potsBroken: number;
  attack(): void;
  teleport(x: number, z: number): void;
  debug: {
    pos(): [number, number];
    nearestGrass(): [number, number] | null;
    nearestSlime(): [number, number] | null;
    totalSlimeHp(): number;
    slimeHits(): number;
    grassRemaining(): number;
  };
}

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

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

test('title -> start -> move -> cut grass -> hit slime, no console errors', async ({ page }) => {
  const errors = await collectErrors(page);

  await page.goto('/');
  await expect(page.locator('#title-screen h1')).toHaveText('BRAMBLEGLEN');
  await page.waitForFunction(() => window.__game?.phase === 'title');

  // start
  await page.locator('#title-screen').click();
  await page.waitForFunction(() => window.__game.phase === 'playing');

  // move with keyboard
  const p0 = await page.evaluate(() => window.__game.debug.pos());
  await page.keyboard.down('w');
  await page.waitForTimeout(500);
  await page.keyboard.up('w');
  const p1 = await page.evaluate(() => window.__game.debug.pos());
  const moved = Math.hypot(p1[0] - p0[0], p1[1] - p0[1]);
  expect(moved).toBeGreaterThan(0.5);

  // cut grass: teleport just south of a tuft (teleport faces the hero up-screen)
  // and swing until the counter moves
  for (let i = 0; i < 5; i++) {
    await page.evaluate(() => {
      const g = window.__game.debug.nearestGrass();
      if (g) window.__game.teleport(g[0], g[1] + 1.1);
    });
    await page.evaluate(() => window.__game.attack());
    await page.waitForTimeout(550);
    const cut = await page.evaluate(() => window.__game.grassCut);
    if (cut > 0) break;
  }
  expect(await page.evaluate(() => window.__game.grassCut)).toBeGreaterThan(0);

  // hit a slime
  for (let i = 0; i < 6; i++) {
    await page.evaluate(() => {
      const s = window.__game.debug.nearestSlime();
      if (s) window.__game.teleport(s[0], s[1] + 1.1);
    });
    await page.evaluate(() => window.__game.attack());
    await page.waitForTimeout(600);
    const hits = await page.evaluate(() => window.__game.debug.slimeHits());
    if (hits > 0) break;
  }
  expect(await page.evaluate(() => window.__game.debug.slimeHits())).toBeGreaterThan(0);

  // pots are also reachable via the same arc check — smash one for good measure
  await page.evaluate(() => window.__game.teleport(10.5, -9.6));
  await page.evaluate(() => window.__game.attack());
  await page.waitForTimeout(550);

  expect(errors).toEqual([]);
});
