import './styles.css';
import './leveled/leveled.css';
import { Game } from './game';
import { HUD } from './hud';
import { Leveled } from './leveled/editor';
import { parseDoc } from './leveled/doc';
import type { LevelDoc } from './leveled/types';
import { makeAdapter, skitterSpec, docToLevel, levelToDoc } from './adapter';
import { initEmbedEditing } from './embed';
import { defaultLevel, LS_KEY, LevelData } from './level';

const app = document.getElementById('app');
if (!app) throw new Error('missing #app');

// boot doc: autosaved v2 doc, legacy v1 save (parseDoc migrates), or default
let doc: LevelDoc;
try {
  const saved = localStorage.getItem(LS_KEY);
  doc = saved ? parseDoc(saved, skitterSpec) : levelToDoc(defaultLevel());
} catch (err) {
  console.warn('[skitterdome] saved level invalid, using default', err);
  doc = levelToDoc(defaultLevel());
}

const game = new Game(app, docToLevel(doc));

// ?edit=1 → remote-controlled edit canvas for a parent editor page
// (editor-slice/PROTOCOL.md): no built-in editor UI, no HUD, no E toggle.
const embedMode = new URLSearchParams(location.search).has('edit');

let editor: Leveled | null = null;
if (embedMode) {
  initEmbedEditing(game);
} else {
  const hud = new HUD(app, {
    onPause: () => game.togglePause(),
    onEdit: () => game.toggleMode(),
  });
  game.hud = hud;
  hud.setHP(game.player?.hp ?? 0, 3);
  hud.setMode(game.mode);

  const adapter = makeAdapter(game);
  const ed = new Leveled(adapter, app, doc);
  editor = ed;
  game.onModeChange = (m) => {
    if (m === 'editor') ed.show();
    else ed.hide();
  };
}

game.start();

// Live-edit seam for the standalone editor (editor-slice). The editor posts
// the active scene flattened to the v2 doc shape, so the shipped parser and
// docToLevel() are reused untouched. In play mode refreshLevel() restarts the
// run on the new layout — which is what a live preview wants.
// MGS NOTE: this is the sibling of the studio's `game-tweaks:update` channel.
window.addEventListener('message', (e: MessageEvent) => {
  const d = e.data as { type?: string; doc?: unknown } | null;
  if (!d || d.type !== 'game-level:update' || d.doc === undefined) return;
  try {
    game.level = docToLevel(parseDoc(d.doc, skitterSpec));
    game.refreshLevel();
  } catch (err) {
    console.warn('[skitterdome] ignored bad level from postMessage', err);
  }
});

// Debug / e2e hook
interface GameHook {
  readonly phase: string;
  readonly mode: string;
  readonly hp: number;
  readonly enemies: number;
  readonly gigas: number;
  readonly rockets: number;
  readonly scraps: number;
  readonly scrap: number;
  readonly orbs: number;
  readonly treeUnlocked: boolean;
  readonly cds: Record<string, number> | null;
  readonly levelJson: LevelData;
  readonly levelDoc: LevelDoc;
  loadLevel(raw: unknown): void;
}
declare global {
  interface Window {
    __game: GameHook;
  }
}
// internal handle for debugging/e2e
(window as unknown as Record<string, unknown>).__gameInstance = game;

window.__game = {
  get phase() {
    return game.phase;
  },
  get mode() {
    return game.mode;
  },
  get hp() {
    return game.player?.hp ?? 0;
  },
  get enemies() {
    return game.enemies.length;
  },
  get gigas() {
    return game.gigaCount;
  },
  get rockets() {
    return game.rockets.length;
  },
  get scraps() {
    return game.scraps.length;
  },
  get scrap() {
    return game.scrapCount;
  },
  get orbs() {
    return game.orbs.length;
  },
  get treeUnlocked() {
    return game.treeUnlocked;
  },
  get cds() {
    const p = game.player;
    return p
      ? { bomb: p.bombCd, rocket: p.rocketCd, tesla: p.teslaCd, blade: p.bladeCd, orb: p.orbCd, dash: p.dashCd }
      : null;
  },
  get levelJson() {
    return JSON.parse(JSON.stringify(game.level)) as LevelData;
  },
  get levelDoc() {
    return JSON.parse(JSON.stringify(editor ? editor.doc : levelToDoc(game.level))) as LevelDoc;
  },
  loadLevel(raw: unknown) {
    if (editor) {
      editor.loadRaw(raw);
    } else {
      game.level = docToLevel(parseDoc(raw, skitterSpec));
      game.refreshLevel();
    }
  },
};
