import './style.css';
import { loadAssets } from './assets';
import { COUNTER_TOP, GUEST_BOTTOM, Game, SLOT_X } from './game';
import { CookingPanel, QUALITY_MULT } from './cooking';
import { Particles } from './particles';
import { drawScene } from './render';
import { clearOverlay, showDayIntro, showRecap, showTitle, showWin, toast, updateHud } from './hud';
import { RECIPES } from './recipes';
import { sfx } from './audio';
import type { Guest, Phase } from './types';

declare global {
  interface Window {
    __game?: unknown;
  }
}

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

function resize(): void {
  const s = Math.min(window.innerWidth / 390, window.innerHeight / 844);
  stage.style.transform = `translate(-50%, -50%) scale(${s})`;
  const pr = Math.min(2.5, (window.devicePixelRatio || 1) * s);
  canvas.width = Math.round(390 * pr);
  canvas.height = Math.round(844 * pr);
  ctx.setTransform(pr, 0, 0, pr, 0, 0);
}
window.addEventListener('resize', resize);
resize();

async function boot(): Promise<void> {
  const assets = await loadAssets();
  const game = new Game();
  const particles = new Particles();
  const panel = new CookingPanel(assets);

  // ---------- order bubbles ----------
  const bubbleEls = [0, 1, 2].map((slot) => {
    const d = document.createElement('div');
    d.className = 'bubble hidden';
    d.dataset.slot = String(slot);
    d.style.left = `${SLOT_X[slot] - 52}px`;
    d.style.top = '258px';
    d.innerHTML =
      '<canvas class="bfood" width="96" height="72"></canvas><div class="bname"></div><div class="pbar"><div class="pfill"></div></div>';
    d.addEventListener('click', () => {
      sfx.unlock();
      const g = game.guests.find((o) => o.slot === slot && o.state === 'waiting');
      if (g) openCooking(g);
    });
    document.getElementById('bubbles')!.appendChild(d);
    return d;
  });

  function refreshBubbles(): void {
    for (let slot = 0; slot < 3; slot++) {
      const el = bubbleEls[slot];
      const g =
        game.phase === 'playing'
          ? game.guests.find((o) => o.slot === slot && o.state === 'waiting')
          : undefined;
      if (!g) {
        el.classList.add('hidden');
        el.dataset.gid = '';
        continue;
      }
      if (el.dataset.gid !== String(g.id)) {
        el.dataset.gid = String(g.id);
        const recipe = RECIPES[g.recipeIdx];
        const bc = el.querySelector('.bfood') as HTMLCanvasElement;
        const bctx = bc.getContext('2d')!;
        bctx.clearRect(0, 0, 96, 72);
        const dish = assets[recipe.dishAsset];
        if (dish) {
          bctx.drawImage(dish, 8, -4, 80, 80);
        } else {
          bctx.font = '44px system-ui, sans-serif';
          bctx.textAlign = 'center';
          bctx.fillText(recipe.emoji, 48, 52);
        }
        (el.querySelector('.bname') as HTMLElement).textContent = recipe.name;
      }
      el.classList.remove('hidden');
      const k = Math.max(0, g.patience / g.patienceMax);
      const fill = el.querySelector('.pfill') as HTMLElement;
      fill.style.width = `${k * 100}%`;
      fill.style.background = k > 0.5 ? '#4caf50' : k > 0.25 ? '#ffb300' : '#e5392f';
      el.classList.toggle('urgent', k < 0.25);
    }
  }

  // ---------- cooking ----------
  function openCooking(g: Guest): void {
    if (game.phase !== 'playing' || panel.isOpen || g.state !== 'waiting') return;
    sfx.tap();
    game.cookingGuestId = g.id;
    panel.open(g, game.cfg().cookSpeed);
  }

  panel.onWrongTap = () => {
    const g = panel.guestId === null ? undefined : game.guestById(panel.guestId);
    if (g) g.patience = Math.max(0.01, g.patience - 1.5);
  };
  panel.onClose = () => {
    game.cookingGuestId = null;
  };
  panel.onServe = (quality, guestId) => {
    const g = game.guestById(guestId);
    if (!g || (g.state !== 'waiting' && g.state !== 'arriving')) {
      toast('they already left 💔');
      return;
    }
    game.serveGuest(g, QUALITY_MULT[quality]);
  };

  // ---------- game events → fx ----------
  game.onServed = (g, amount) => {
    const tx = SLOT_X[g.slot];
    const recipe = RECIPES[g.recipeIdx];
    game.flying.push({
      asset: recipe.dishAsset,
      emoji: recipe.emoji,
      fromX: 195,
      fromY: 770,
      toX: tx,
      toY: COUNTER_TOP - 14,
      x: 195,
      y: 770,
      t: 0,
    });
    sfx.coin();
    window.setTimeout(() => {
      particles.coinBurst(tx, COUNTER_TOP - 60);
      particles.text(tx, COUNTER_TOP - 95, `+${amount}`);
      for (let i = 0; i < 5; i++) particles.steam(tx, COUNTER_TOP - 30);
    }, 380);
  };

  game.onGuestLost = (g) => {
    sfx.angry();
    particles.emoji(SLOT_X[g.slot], GUEST_BOTTOM - 235, '💢', 34);
    particles.text(SLOT_X[g.slot], GUEST_BOTTOM - 200, '-★', '#ff7a6e');
    if (panel.isOpen && panel.guestId === g.id) {
      panel.close();
      toast('they left before it was ready 💔');
    }
  };

  // ---------- canvas taps (tap the guest directly) ----------
  canvas.addEventListener('pointerdown', (e) => {
    sfx.unlock();
    if (game.phase !== 'playing' || panel.isOpen) return;
    const r = canvas.getBoundingClientRect();
    const x = ((e.clientX - r.left) / r.width) * 390;
    const y = ((e.clientY - r.top) / r.height) * 844;
    if (y < 320 || y > 620) return;
    const g = game.guests.find((o) => o.state === 'waiting' && Math.abs(o.x - x) < 56);
    if (g) openCooking(g);
  });

  // ---------- phase → overlay sync ----------
  let lastPhase: Phase | '' = '';
  function syncPhase(): void {
    if (game.phase === lastPhase) return;
    lastPhase = game.phase;
    switch (game.phase) {
      case 'title':
        showTitle(game, () => {
          sfx.unlock();
          sfx.tap();
          game.phase = 'dayintro';
        });
        break;
      case 'dayintro':
        showDayIntro(game, () => {
          sfx.unlock();
          sfx.tap();
          game.startDay();
        });
        break;
      case 'playing':
        clearOverlay();
        break;
      case 'recap':
        if (panel.isOpen) panel.close();
        showRecap(game, () => {
          sfx.tap();
          game.advance();
        });
        break;
      case 'win':
        showWin(game, () => {
          sfx.tap();
          game.resetToTitle();
        });
        break;
    }
  }

  // ---------- ambient steam from the coffee machine ----------
  let steamAcc = 0;

  // ---------- main loop ----------
  let last = performance.now();
  function frame(now: number): void {
    const dt = Math.min(0.05, (now - last) / 1000);
    last = now;
    game.update(dt);
    panel.update(dt);
    particles.update(dt);
    if (game.phase === 'playing') {
      steamAcc += dt;
      if (steamAcc > 0.65) {
        steamAcc = 0;
        particles.steam(332, 308);
      }
    }
    syncPhase();
    drawScene(ctx, assets, game, particles, now / 1000);
    updateHud(game);
    refreshBubbles();
    const cg = panel.isOpen && panel.guestId !== null ? game.guestById(panel.guestId) : undefined;
    if (cg) {
      const k = Math.max(0, cg.patience / cg.patienceMax);
      const fill = document.getElementById('cp-pfill')!;
      fill.style.width = `${k * 100}%`;
      fill.style.background = k > 0.5 ? '#4caf50' : k > 0.25 ? '#ffb300' : '#e5392f';
    }
    requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);

  // ---------- e2e hook ----------
  window.__game = {
    get phase() {
      return game.phase;
    },
    get day() {
      return game.day;
    },
    get coins() {
      return game.coins;
    },
    get guests() {
      return game.guests.map((g) => ({
        id: g.id,
        slot: g.slot,
        state: g.state,
        recipe: RECIPES[g.recipeIdx].id,
        patience: g.patience,
      }));
    },
    get activeRecipe() {
      return panel.ingredientList;
    },
    serveTest: () => game.serveTest(),
    debugEndDay: () => {
      if (game.phase === 'playing') game.timeLeft = 0.01;
    },
    start: () => {
      if (game.phase === 'title') game.phase = 'dayintro';
      else if (game.phase === 'dayintro') game.startDay();
    },
  };
}

void boot();
