/** Boot, main loop, and the window.__game debug hook. */
import { loadAssets, type Assets } from './assets.ts';
import {
  newGame, loadGame, hasSave, clearSave, save, useTool, useToolOnTile, setTool, sleep, wake,
  tickClock, movePlayer, targetTile, tileAt, TOOLS, type GameState,
} from './game.ts';
import { TILE } from './world.ts';
import { render, computeCamera } from './render.ts';
import { initHUD, updateHUD, updateClock, showContinue } from './hud.ts';
import { initInput, moveVector } from './input.ts';
import { initAudio, updateAmbience } from './audio.ts';
import { updateParticles } from './particles.ts';
import { updateAmbient } from './ambient.ts';

const canvas = document.getElementById('game') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;

let g: GameState = newGame();
let assets: Assets;
let cw = 0;
let ch = 0;

function resize(): void {
  const dpr = Math.min(window.devicePixelRatio || 1, 2);
  cw = window.innerWidth;
  ch = window.innerHeight;
  canvas.width = Math.round(cw * dpr);
  canvas.height = Math.round(ch * dpr);
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}

window.addEventListener('resize', resize);
resize();

/** screen px -> world tile under the cursor */
function tileFromScreen(sx: number, sy: number): { x: number; y: number } {
  const cam = computeCamera(g, cw, ch);
  return {
    x: Math.floor((sx / cam.zoom + cam.x) / TILE),
    y: Math.floor((sy / cam.zoom + cam.y) / TILE),
  };
}

// Stardew-style click-to-act: tap/click a tile next to (or under) the farmer
// to swing the current tool at it.
canvas.addEventListener('pointerdown', (e) => {
  if (g.phase !== 'play') return;
  const t = tileFromScreen(e.clientX, e.clientY);
  useToolOnTile(g, t.x, t.y);
});

// ---------------------------------------------------------------------------
// debug hook (also used by the e2e suite)
// ---------------------------------------------------------------------------
declare global {
  interface Window {
    __game: {
      phase: string;
      day: number;
      energy: number;
      money: number;
      inventory: Record<string, number>;
      playerPos: { x: number; y: number };
      time: number;
      tool: string;
      targetTile: { x: number; y: number };
      tileAt: (x: number, y: number) => { kind: string; watered: boolean; tree: boolean; crop: { type: string; stage: number } | null } | null;
      screenPos: (x: number, y: number) => { x: number; y: number };
      debugSetTime: (t: number) => void;
    };
  }
}

window.__game = {
  get phase() { return g.phase; },
  get day() { return g.day; },
  get energy() { return g.energy; },
  get money() { return g.money; },
  get inventory() { return { ...g.inventory } as Record<string, number>; },
  get playerPos() { return { x: g.player.x, y: g.player.y }; },
  get time() { return g.time; },
  get tool() { return g.tool; },
  get targetTile() { return targetTile(g); },
  tileAt(x: number, y: number) {
    const t = tileAt(g, x, y);
    return t
      ? { kind: t.kind, watered: t.watered, tree: t.tree, crop: t.crop ? { type: t.crop.type, stage: t.crop.stage } : null }
      : null;
  },
  /** screen px of a tile's centre (for click tests) */
  screenPos(x: number, y: number) {
    const cam = computeCamera(g, cw, ch);
    return {
      x: ((x + 0.5) * TILE - cam.x) * cam.zoom,
      y: ((y + 0.5) * TILE - cam.y) * cam.zoom,
    };
  },
  debugSetTime(t: number) {
    g.time = Math.max(0, Math.min(0.999, t));
  },
};

// ---------------------------------------------------------------------------
// boot
// ---------------------------------------------------------------------------

async function boot(): Promise<void> {
  assets = await loadAssets();

  initHUD(assets, {
    onNew: () => {
      clearSave();
      g = newGame();
      g.phase = 'play';
      g.hudDirty = true;
      g.tilesDirty = true;
      save(g);
    },
    onContinue: () => {
      const loaded = loadGame();
      if (loaded) g = loaded;
      else g.phase = 'play';
      g.hudDirty = true;
      g.tilesDirty = true;
    },
    onWake: () => wake(g),
    onSleep: () => sleep(g),
    onTool: (tool) => setTool(g, tool),
  });

  initInput({
    onAction: () => useTool(g),
    onTool: (i) => setTool(g, TOOLS[i]),
    onAnyGesture: () => initAudio(),
  });

  showContinue(hasSave());
  updateHUD(g);

  if (window.matchMedia('(pointer: coarse)').matches) {
    const hint = document.getElementById('title-hint');
    if (hint) hint.textContent = 'drag left side to walk · tap a tile or ✦ to use tool · tap a slot to switch · 💤 to sleep';
  }

  let last = performance.now();
  let elapsed = 0;
  let saveTimer = 0;

  function frame(now: number): void {
    const dt = Math.min((now - last) / 1000, 0.05);
    last = now;
    elapsed += dt;

    if (g.phase === 'play') {
      const v = moveVector();
      movePlayer(g, v.x, v.y, dt);
      tickClock(g, dt);
      g.toolAnim = Math.max(0, g.toolAnim - dt);
      saveTimer += dt;
      if (saveTimer > 3) {
        saveTimer = 0;
        save(g); // catch player-position drift between actions
      }
    }
    updateParticles(dt);
    updateAmbient(g, dt);
    if (g.phase === 'play') updateAmbience(g.time, dt);

    render(ctx, g, assets, cw, ch, dt, elapsed);

    if (g.phase !== 'title') updateClock(g);
    if (g.hudDirty) {
      g.hudDirty = false;
      updateHUD(g);
    }
    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);

  window.addEventListener('pagehide', () => save(g));
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') save(g);
  });
}

void boot();
