export type AssetMap = Record<string, HTMLCanvasElement | null>;

/** Measured at load: where the counter plate's visible top edge sits (0..1 of its height). */
export const meta = { counterEdge: 0.45 };

interface Spec {
  file: string;
  chroma?: boolean;
  maxSize?: number;
}

const SPECS: Record<string, Spec> = {
  bg: { file: 'bg.png' },
  counter: { file: 'counter.png', chroma: true },
  guest0: { file: 'guest0.png', chroma: true, maxSize: 512 },
  guest1: { file: 'guest1.png', chroma: true, maxSize: 512 },
  guest2: { file: 'guest2.png', chroma: true, maxSize: 512 },
  guest3: { file: 'guest3.png', chroma: true, maxSize: 512 },
  dish_pancakes: { file: 'dish_pancakes.png', chroma: true, maxSize: 512 },
  dish_eggs: { file: 'dish_eggs.png', chroma: true, maxSize: 512 },
  dish_burger: { file: 'dish_burger.png', chroma: true, maxSize: 512 },
  dish_pie: { file: 'dish_pie.png', chroma: true, maxSize: 512 },
  griddle: { file: 'griddle.png', maxSize: 512 },
};

/** First row (as a fraction of height) where the keyed image becomes opaque, sampled mid-column. */
function findTopEdge(cv: HTMLCanvasElement): number {
  const ctx = cv.getContext('2d', { willReadFrequently: true })!;
  const x = Math.floor(cv.width / 2);
  const col = ctx.getImageData(x, 0, 1, cv.height).data;
  for (let y = 0; y < cv.height; y++) {
    if (col[y * 4 + 3] > 40) return y / cv.height;
  }
  return 0.45;
}

function loadImage(src: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = () => reject(new Error(`failed to load ${src}`));
    img.src = src;
  });
}

/** Draw to a canvas, optionally downscaled, optionally keying out flat magenta (#FF00FF). */
function toCanvas(img: HTMLImageElement, spec: Spec): HTMLCanvasElement {
  let w = img.naturalWidth;
  let h = img.naturalHeight;
  if (spec.maxSize && Math.max(w, h) > spec.maxSize) {
    const s = spec.maxSize / Math.max(w, h);
    w = Math.round(w * s);
    h = Math.round(h * s);
  }
  const cv = document.createElement('canvas');
  cv.width = w;
  cv.height = h;
  const ctx = cv.getContext('2d', { willReadFrequently: true })!;
  ctx.drawImage(img, 0, 0, w, h);
  if (spec.chroma) {
    const data = ctx.getImageData(0, 0, w, h);
    const px = data.data;
    for (let i = 0; i < px.length; i += 4) {
      const r = px[i];
      const g = px[i + 1];
      const b = px[i + 2];
      // magenta-ness: both r and b high while g low
      const m = Math.min(r, b) - g;
      if (m > 96) {
        px[i + 3] = 0;
      } else if (m > 40) {
        // soften edges and pull the magenta fringe out of the color
        px[i + 3] = Math.round(px[i + 3] * (1 - (m - 40) / 70));
        const avg = (r + g + b) / 3;
        px[i] = Math.round((r + avg) / 2);
        px[i + 2] = Math.round((b + avg) / 2);
      }
    }
    ctx.putImageData(data, 0, 0);
  }
  return cv;
}

export async function loadAssets(): Promise<AssetMap> {
  let manifest: Record<string, boolean> = {};
  try {
    const res = await fetch('./art/manifest.json');
    if (res.ok) manifest = (await res.json()) as Record<string, boolean>;
  } catch {
    // no art yet — placeholders will render
  }
  const out: AssetMap = {};
  await Promise.all(
    Object.entries(SPECS).map(async ([key, spec]) => {
      if (!manifest[spec.file]) {
        out[key] = null;
        return;
      }
      try {
        out[key] = toCanvas(await loadImage(`./art/${spec.file}`), spec);
        if (key === 'counter' && out[key]) meta.counterEdge = findTopEdge(out[key]);
      } catch {
        out[key] = null;
      }
    }),
  );
  return out;
}
