import { ING, RECIPES } from './recipes';
import { sfx } from './audio';
import type { AssetMap } from './assets';
import type { Guest, IngredientId, Recipe } from './types';

export type CookQuality = 'perfect' | 'good' | 'overcooked';
export const QUALITY_MULT: Record<CookQuality, number> = {
  perfect: 1.5,
  good: 1,
  overcooked: 0.6,
};

const ALL_INGS = Object.keys(ING) as IngredientId[];

/** Deterministic little PRNG so the decoy grid is stable per-order. */
function lcg(seed: number): () => number {
  let s = (seed * 9301 + 49297) % 233280;
  return () => {
    s = (s * 9301 + 49297) % 233280;
    return s / 233280;
  };
}

export class CookingPanel {
  private root = document.getElementById('cookpanel')!;
  private card = document.getElementById('cp-card')!;
  private nameEl = document.getElementById('cp-name')!;
  private orderEl = document.getElementById('cp-order')!;
  private ingsEl = document.getElementById('cp-ings')!;
  private cookEl = document.getElementById('cp-cook')!;
  private doneEl = document.getElementById('cp-done')!;
  private resultEl = document.getElementById('cp-result')!;
  private barCv = document.getElementById('cp-bar') as HTMLCanvasElement;
  private dishCv = document.getElementById('cp-dish') as HTMLCanvasElement;

  guestId: number | null = null;
  recipe: Recipe | null = null;
  private step = 0;
  private mode: 'assemble' | 'cook' | 'done' = 'assemble';
  private cookT = 0;
  private cookSpeed = 1;
  private quality: CookQuality | null = null;
  private flashT = 0;

  private sparks: { x: number; y: number; vx: number; vy: number; life: number }[] = [];

  onWrongTap: () => void = () => {};
  onServe: (quality: CookQuality, guestId: number) => void = () => {};
  onClose: () => void = () => {};

  constructor(private assets: AssetMap) {
    document.getElementById('cp-close')!.addEventListener('click', () => {
      sfx.tap();
      this.close();
      this.onClose();
    });
    document.getElementById('cp-tap')!.addEventListener('click', () => this.tapCook());
    this.barCv.addEventListener('pointerdown', () => this.tapCook());
    document.getElementById('cp-serve')!.addEventListener('click', () => {
      if (this.mode !== 'done' || !this.quality || this.guestId === null) return;
      sfx.serve();
      const q = this.quality;
      const gid = this.guestId;
      this.close();
      this.onServe(q, gid);
    });
  }

  get isOpen(): boolean {
    return this.root.classList.contains('open');
  }

  get ingredientList(): IngredientId[] {
    return this.recipe ? [...this.recipe.ingredients] : [];
  }

  open(guest: Guest, cookSpeed: number): void {
    this.guestId = guest.id;
    this.recipe = RECIPES[guest.recipeIdx];
    this.step = 0;
    this.mode = 'assemble';
    this.cookT = 0;
    this.cookSpeed = cookSpeed;
    this.quality = null;
    this.nameEl.textContent = `${this.recipe.emoji} ${this.recipe.name}`;
    this.buildOrderSlots();
    this.buildIngredientGrid(guest.id);
    this.cookEl.classList.add('hidden');
    this.doneEl.classList.add('hidden');
    this.ingsEl.classList.remove('hidden');
    this.orderEl.classList.remove('hidden');
    this.root.classList.add('open');
  }

  close(): void {
    this.root.classList.remove('open');
    this.guestId = null;
    this.recipe = null;
  }

  private buildOrderSlots(): void {
    this.orderEl.innerHTML = '';
    for (const ing of this.recipe!.ingredients) {
      const d = document.createElement('div');
      d.className = 'oslot';
      d.textContent = '?';
      d.dataset.want = ing;
      this.orderEl.appendChild(d);
    }
  }

  private buildIngredientGrid(seed: number): void {
    const recipe = this.recipe!;
    const rand = lcg(seed * 7 + 13);
    const pool = ALL_INGS.filter((i) => !recipe.ingredients.includes(i));
    // pick decoys to fill a 6-button grid
    const decoys: IngredientId[] = [];
    while (decoys.length + recipe.ingredients.length < 6 && pool.length > 0) {
      const k = Math.floor(rand() * pool.length);
      decoys.push(pool.splice(k, 1)[0]);
    }
    const grid = [...recipe.ingredients, ...decoys];
    for (let i = grid.length - 1; i > 0; i--) {
      const j = Math.floor(rand() * (i + 1));
      [grid[i], grid[j]] = [grid[j], grid[i]];
    }
    this.ingsEl.innerHTML = '';
    for (const ing of grid) {
      const b = document.createElement('button');
      b.className = 'ing';
      b.dataset.ing = ing;
      b.innerHTML = `${ING[ing].emoji}<span class="ilabel">${ING[ing].label}</span>`;
      b.addEventListener('click', () => this.tapIngredient(ing, b));
      this.ingsEl.appendChild(b);
    }
  }

  private tapIngredient(ing: IngredientId, btn: HTMLButtonElement): void {
    if (this.mode !== 'assemble' || !this.recipe) return;
    const want = this.recipe.ingredients[this.step];
    if (ing === want) {
      sfx.place();
      btn.classList.add('used');
      const slot = this.orderEl.children[this.step] as HTMLElement;
      slot.textContent = ING[ing].emoji;
      slot.classList.add('filled');
      this.step++;
      if (this.step >= this.recipe.ingredients.length) this.startCook();
    } else {
      sfx.wrong();
      this.card.classList.remove('shake');
      void this.card.offsetWidth; // restart animation
      this.card.classList.add('shake');
      this.onWrongTap();
    }
  }

  private startCook(): void {
    this.mode = 'cook';
    this.cookT = 0;
    this.ingsEl.classList.add('hidden');
    this.cookEl.classList.remove('hidden');
  }

  /** Marker position 0..1, ping-pong. */
  private markerPos(): number {
    const t = this.cookT * this.cookSpeed * 0.85;
    const k = t % 2;
    return k < 1 ? k : 2 - k;
  }

  private tapCook(): void {
    if (this.mode !== 'cook') return;
    const p = this.markerPos();
    const d = Math.abs(p - 0.5);
    this.quality = d < 0.09 ? 'perfect' : d < 0.24 ? 'good' : 'overcooked';
    if (this.quality === 'perfect') sfx.perfect();
    else if (this.quality === 'good') sfx.good();
    else sfx.over();
    this.flashT = 0.35;
    this.mode = 'done';
    window.setTimeout(() => {
      this.cookEl.classList.add('hidden');
      this.orderEl.classList.add('hidden');
      this.doneEl.classList.remove('hidden');
      this.drawDish();
      this.resultEl.textContent =
        this.quality === 'perfect'
          ? '✨ PERFECT! +50% tip'
          : this.quality === 'good'
            ? '😊 Nice and golden!'
            : '🔥 A little crispy...';
    }, 380);
  }

  private drawDish(): void {
    const ctx = this.dishCv.getContext('2d')!;
    ctx.clearRect(0, 0, 180, 180);
    const img = this.assets[this.recipe!.dishAsset];
    if (img) {
      ctx.drawImage(img, 0, 0, 180, 180);
    } else {
      ctx.fillStyle = '#fffdf8';
      ctx.strokeStyle = '#cdb89a';
      ctx.lineWidth = 4;
      ctx.beginPath();
      ctx.ellipse(90, 110, 78, 38, 0, 0, Math.PI * 2);
      ctx.fill();
      ctx.stroke();
      ctx.font = '74px system-ui, sans-serif';
      ctx.textAlign = 'center';
      ctx.fillText(this.recipe!.emoji, 90, 118);
    }
    if (this.quality === 'overcooked') {
      ctx.globalAlpha = 0.3;
      ctx.fillStyle = '#321c08';
      ctx.beginPath();
      ctx.ellipse(90, 95, 70, 56, 0, 0, Math.PI * 2);
      ctx.fill();
      ctx.globalAlpha = 1;
    }
  }

  /** Called every frame from the main loop; draws the cook bar + sizzle. */
  update(dt: number): void {
    if (!this.isOpen || this.mode !== 'cook') {
      if (this.flashT > 0) this.flashT -= dt;
      this.sparks = [];
      return;
    }
    this.cookT += dt;
    const ctx = this.barCv.getContext('2d')!;
    const W = 340;
    const H = 120;
    // griddle backdrop
    const griddle = this.assets['griddle'];
    if (griddle) {
      ctx.drawImage(griddle, 0, -110, W, W);
    } else {
      ctx.fillStyle = '#2b2b33';
      ctx.fillRect(0, 0, W, H);
      ctx.fillStyle = '#23232a';
      for (let i = 0; i < 5; i++) ctx.fillRect(0, i * 26 + 8, W, 3);
    }
    // zones
    const barY = 70;
    const barH = 26;
    const zone = (a: number, b: number, color: string) => {
      ctx.fillStyle = color;
      ctx.fillRect(20 + a * 300, barY, (b - a) * 300, barH);
    };
    zone(0, 1, '#6b5238');
    zone(0.26, 0.74, '#d99a3f');
    zone(0.41, 0.59, '#ffd24a');
    ctx.fillStyle = 'rgba(255,255,255,0.85)';
    ctx.fillRect(20 + 0.41 * 300 - 1, barY - 5, 2, barH + 10);
    ctx.fillRect(20 + 0.59 * 300 - 1, barY - 5, 2, barH + 10);
    ctx.strokeStyle = '#1c120a';
    ctx.lineWidth = 3;
    ctx.strokeRect(20, barY, 300, barH);
    // food icon riding the marker
    const p = this.markerPos();
    const mx = 20 + p * 300;
    ctx.font = '34px system-ui, sans-serif';
    ctx.textAlign = 'center';
    ctx.fillText(this.recipe?.emoji ?? '🍳', mx, barY - 14);
    // marker flame
    ctx.fillStyle = '#ff8a3d';
    ctx.beginPath();
    ctx.moveTo(mx, barY - 6);
    ctx.lineTo(mx - 8, barY + barH + 8);
    ctx.lineTo(mx + 8, barY + barH + 8);
    ctx.closePath();
    ctx.fill();
    // sizzle sparks rise from the marker
    if (Math.random() < 0.6) {
      this.sparks.push({
        x: mx + (Math.random() - 0.5) * 14,
        y: barY + barH + 4,
        vx: (Math.random() - 0.5) * 50,
        vy: -40 - Math.random() * 70,
        life: 0.4 + Math.random() * 0.2,
      });
    }
    for (const s of [...this.sparks]) {
      s.life -= dt;
      s.x += s.vx * dt;
      s.y += s.vy * dt;
      s.vy += 200 * dt;
      if (s.life <= 0) {
        this.sparks = this.sparks.filter((o) => o !== s);
        continue;
      }
      ctx.fillStyle = Math.random() < 0.5 ? '#ffd24a' : '#ff8a3d';
      ctx.fillRect(s.x - 2, s.y - 2, 4, 4);
    }
  }
}
