/**
 * Asset loading: gpt-image-2 PNGs (chroma-keyed where flagged) converted to
 * Uint32Array textures for the software renderer, plus procedural textures
 * (floor, ceiling, keycard, exit beacon, puffs) drawn at boot.
 *
 * Pixel format everywhere: little-endian RGBA bytes viewed as u32 = 0xAABBGGRR.
 */

export interface Tex {
  w: number;
  h: number;
  px: Uint32Array;
}

export interface Assets {
  /** wall textures indexed by cell value: [_, hull, cable, grate] */
  walls: Tex[];
  door: Tex;
  floor: Tex;
  ceil: Tex;
  husk: Tex;
  stalker: Tex;
  brute: Tex;
  medkit: Tex;
  ammo: Tex;
  keycard: Tex;
  exit: Tex;
  puffBlood: Tex;
  puffSpark: Tex;
  projectile: Tex;
  gun: HTMLCanvasElement | null;
  backdropUrl: string | null;
}

function loadImage(url: string): Promise<HTMLImageElement | null> {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = () => resolve(null);
    img.src = url;
  });
}

/** Remove solid-magenta background and despill fringes, in place. */
function chromaKey(d: ImageData): void {
  const a = d.data;
  for (let i = 0; i < a.length; i += 4) {
    const r = a[i], g = a[i + 1], b = a[i + 2];
    // magenta-ish: red & blue high, green much lower
    if (r > 120 && b > 120 && g < (r + b) * 0.33) {
      a[i + 3] = 0;
    } else if (r > 80 && b > 80 && g < (r + b) * 0.42) {
      // despill edge: pull magenta fringe toward neutral
      const m = (r + b) >> 1;
      a[i] = (r + g) >> 1;
      a[i + 2] = (b + g) >> 1;
      a[i + 1] = Math.min(255, (g + m * 0.2) | 0);
    }
  }
}

function imgToTex(img: HTMLImageElement, size: number, chroma: boolean): Tex {
  const c = document.createElement('canvas');
  c.width = size;
  c.height = size;
  const ctx = c.getContext('2d')!;
  ctx.drawImage(img, 0, 0, size, size);
  const d = ctx.getImageData(0, 0, size, size);
  if (chroma) chromaKey(d);
  return { w: size, h: size, px: new Uint32Array(d.data.buffer) };
}

function canvasToTex(c: HTMLCanvasElement): Tex {
  const ctx = c.getContext('2d')!;
  const d = ctx.getImageData(0, 0, c.width, c.height);
  return { w: c.width, h: c.height, px: new Uint32Array(d.data.buffer) };
}

function makeCanvas(size: number): [HTMLCanvasElement, CanvasRenderingContext2D] {
  const c = document.createElement('canvas');
  c.width = size;
  c.height = size;
  return [c, c.getContext('2d')!];
}

/** deterministic hash noise */
function hash(x: number, y: number): number {
  let h = (x * 374761393 + y * 668265263) | 0;
  h = ((h ^ (h >> 13)) * 1274126177) | 0;
  return ((h ^ (h >> 16)) >>> 0) / 4294967295;
}

/** Fallback wall texture if a generation failed: tinted plate noise. */
function makeFallbackWall(r: number, g: number, b: number): Tex {
  const [c, ctx] = makeCanvas(256);
  ctx.fillStyle = `rgb(${r},${g},${b})`;
  ctx.fillRect(0, 0, 256, 256);
  for (let y = 0; y < 256; y += 4) {
    for (let x = 0; x < 256; x += 4) {
      const n = hash(x, y) * 0.35 + 0.75;
      ctx.fillStyle = `rgba(${(r * n) | 0},${(g * n) | 0},${(b * n) | 0},1)`;
      ctx.fillRect(x, y, 4, 4);
    }
  }
  ctx.strokeStyle = 'rgba(0,0,0,0.45)';
  ctx.lineWidth = 3;
  for (let i = 0; i <= 256; i += 64) {
    ctx.strokeRect(0, i, 256, 64);
  }
  return canvasToTex(c);
}

function makeFloorTex(): Tex {
  const [c, ctx] = makeCanvas(256);
  // dark steel deck plates with rust noise + seams
  ctx.fillStyle = '#26211c';
  ctx.fillRect(0, 0, 256, 256);
  for (let y = 0; y < 256; y += 2) {
    for (let x = 0; x < 256; x += 2) {
      const n = hash(x, y);
      const rust = hash(x >> 4, y >> 4);
      const base = 28 + n * 18;
      const r = base + rust * 26;
      const g = base + rust * 12;
      const b = base * 0.9;
      ctx.fillStyle = `rgb(${r | 0},${g | 0},${b | 0})`;
      ctx.fillRect(x, y, 2, 2);
    }
  }
  // plate seams
  ctx.strokeStyle = 'rgba(8,6,5,0.9)';
  ctx.lineWidth = 2;
  for (let i = 0; i <= 256; i += 64) {
    ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, 256); ctx.stroke();
    ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(256, i); ctx.stroke();
  }
  // rivets at seam corners
  ctx.fillStyle = 'rgba(70,60,50,0.9)';
  for (let y = 0; y <= 256; y += 64) {
    for (let x = 0; x <= 256; x += 64) {
      ctx.beginPath(); ctx.arc((x + 6) % 256, (y + 6) % 256, 2.5, 0, 7); ctx.fill();
    }
  }
  return canvasToTex(c);
}

function makeCeilTex(): Tex {
  const [c, ctx] = makeCanvas(256);
  ctx.fillStyle = '#15130f';
  ctx.fillRect(0, 0, 256, 256);
  for (let y = 0; y < 256; y += 2) {
    for (let x = 0; x < 256; x += 2) {
      const n = hash(x + 999, y);
      const v = 16 + n * 14;
      ctx.fillStyle = `rgb(${v | 0},${(v * 0.95) | 0},${(v * 0.85) | 0})`;
      ctx.fillRect(x, y, 2, 2);
    }
  }
  // panel grid + vents
  ctx.strokeStyle = 'rgba(5,4,3,0.95)';
  ctx.lineWidth = 3;
  for (let i = 0; i <= 256; i += 128) {
    ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, 256); ctx.stroke();
    ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(256, i); ctx.stroke();
  }
  // one dim light strip per tile
  const grad = ctx.createLinearGradient(64, 0, 192, 0);
  grad.addColorStop(0, 'rgba(120,90,50,0)');
  grad.addColorStop(0.5, 'rgba(190,140,70,0.55)');
  grad.addColorStop(1, 'rgba(120,90,50,0)');
  ctx.fillStyle = grad;
  ctx.fillRect(64, 58, 128, 12);
  ctx.fillRect(64, 186, 128, 12);
  return canvasToTex(c);
}

function makeKeycardTex(): Tex {
  const [c, ctx] = makeCanvas(64);
  ctx.clearRect(0, 0, 64, 64);
  ctx.save();
  ctx.translate(32, 36);
  ctx.rotate(-0.25);
  ctx.fillStyle = '#c91f1f';
  ctx.fillRect(-18, -12, 36, 24);
  ctx.fillStyle = '#f5e9d0';
  ctx.fillRect(-18, -4, 36, 5);
  ctx.fillStyle = '#7a0f0f';
  ctx.fillRect(-18, 6, 36, 6);
  ctx.fillStyle = '#ffd9a0';
  ctx.fillRect(8, -10, 7, 5);
  ctx.restore();
  // glow
  const g = ctx.createRadialGradient(32, 34, 4, 32, 34, 30);
  g.addColorStop(0, 'rgba(255,60,60,0.35)');
  g.addColorStop(1, 'rgba(255,60,60,0)');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, 64, 64);
  return canvasToTex(c);
}

function makeExitTex(): Tex {
  const [c, ctx] = makeCanvas(128);
  ctx.clearRect(0, 0, 128, 128);
  // glowing green hatch frame
  const g = ctx.createRadialGradient(64, 70, 6, 64, 70, 60);
  g.addColorStop(0, 'rgba(120,255,160,0.9)');
  g.addColorStop(0.5, 'rgba(40,200,90,0.35)');
  g.addColorStop(1, 'rgba(20,120,50,0)');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, 128, 128);
  ctx.strokeStyle = '#9cffb0';
  ctx.lineWidth = 5;
  ctx.strokeRect(34, 30, 60, 80);
  ctx.strokeStyle = 'rgba(40,255,120,0.8)';
  ctx.lineWidth = 2;
  ctx.strokeRect(42, 38, 44, 64);
  ctx.fillStyle = '#caffd6';
  ctx.font = 'bold 20px monospace';
  ctx.textAlign = 'center';
  ctx.fillText('EXIT', 64, 76);
  return canvasToTex(c);
}

function makePuffTex(r: number, g: number, b: number): Tex {
  const [c, ctx] = makeCanvas(32);
  ctx.clearRect(0, 0, 32, 32);
  const gr = ctx.createRadialGradient(16, 16, 1, 16, 16, 15);
  gr.addColorStop(0, `rgba(${r},${g},${b},0.95)`);
  gr.addColorStop(0.6, `rgba(${r},${g},${b},0.5)`);
  gr.addColorStop(1, `rgba(${r},${g},${b},0)`);
  ctx.fillStyle = gr;
  ctx.fillRect(0, 0, 32, 32);
  return canvasToTex(c);
}

function makeProjectileTex(): Tex {
  const [c, ctx] = makeCanvas(32);
  ctx.clearRect(0, 0, 32, 32);
  const gr = ctx.createRadialGradient(16, 16, 1, 16, 16, 15);
  gr.addColorStop(0, 'rgba(255,240,200,1)');
  gr.addColorStop(0.35, 'rgba(255,120,40,0.9)');
  gr.addColorStop(1, 'rgba(180,20,10,0)');
  ctx.fillStyle = gr;
  ctx.fillRect(0, 0, 32, 32);
  return canvasToTex(c);
}

function processGun(img: HTMLImageElement): HTMLCanvasElement {
  const c = document.createElement('canvas');
  c.width = 512;
  c.height = 512;
  const ctx = c.getContext('2d')!;
  ctx.drawImage(img, 0, 0, 512, 512);
  const d = ctx.getImageData(0, 0, 512, 512);
  chromaKey(d);
  ctx.putImageData(d, 0, 0);
  return c;
}

const DIR = 'sprites/';

export async function loadAssets(): Promise<Assets> {
  const names = [
    'wall-hull', 'wall-cable', 'wall-grate', 'door',
    'enemy-husk', 'enemy-stalker', 'enemy-brute',
    'gun', 'pickup-medkit', 'pickup-ammo', 'backdrop',
  ] as const;
  const imgs = await Promise.all(names.map((n) => loadImage(`${DIR}${n}.png`)));
  const [hull, cable, grate, door, husk, stalker, brute, gun, medkit, ammo, backdrop] = imgs;

  const wallOr = (img: HTMLImageElement | null, fb: [number, number, number]): Tex =>
    img ? imgToTex(img, 512, false) : makeFallbackWall(...fb);
  const spriteOr = (img: HTMLImageElement | null): Tex =>
    img ? imgToTex(img, 512, true) : makePuffTex(200, 40, 200);

  return {
    walls: [
      makeFallbackWall(40, 40, 40), // index 0 unused
      wallOr(hull, [96, 62, 38]),
      wallOr(cable, [70, 66, 60]),
      wallOr(grate, [80, 30, 26]),
    ],
    door: door ? imgToTex(door, 512, false) : makeFallbackWall(110, 96, 40),
    floor: makeFloorTex(),
    ceil: makeCeilTex(),
    husk: spriteOr(husk),
    stalker: spriteOr(stalker),
    brute: spriteOr(brute),
    medkit: spriteOr(medkit),
    ammo: spriteOr(ammo),
    keycard: makeKeycardTex(),
    exit: makeExitTex(),
    puffBlood: makePuffTex(190, 25, 20),
    puffSpark: makePuffTex(255, 210, 120),
    projectile: makeProjectileTex(),
    gun: gun ? processGun(gun) : null,
    backdropUrl: backdrop ? `${DIR}backdrop.png` : null,
  };
}
