// Pure half of the paper-doll renderer: Patron -> list of coloured rects.
// No Phaser imports — fully unit-testable, deterministic per (patron, pose).
// doll.ts paints these plans into textures; real pixel art later replaces the
// painter while THIS data contract (and renderDoll's signature) stays put.

import { PALETTE } from '../data/outfits';
import { drunkStage } from '../rules/drunk';
import type { Patron } from '../data/types';

export type DollPose = 'queue' | 'idPhoto' | 'floorTopDown';

export interface PlanRect {
  x: number;
  y: number;
  w: number;
  h: number;
  colour: number;
}

export interface DollPlan {
  width: number;
  height: number;
  rects: PlanRect[];
  /** render-time tell: sway amplitude in px (0 = steady) */
  swayPx: number;
}

export const DOLL_SIZES: Record<DollPose, { width: number; height: number }> = {
  queue: { width: 32, height: 48 },
  idPhoto: { width: 24, height: 24 },
  floorTopDown: { width: 16, height: 16 },
};

const SKIN_TONES = [0xf1c9a5, 0xd9a066, 0xa5673f, 0x6b4423, 0x8d5524, 0xe8b088] as const;

// A tiny deterministic hash so body build/skin derive from dollSeed without
// consuming RNG stream state (plans must be reproducible in isolation).
function mix(seed: number, salt: number): number {
  let h = (seed ^ (salt * 0x9e3779b9)) >>> 0;
  h = Math.imul(h ^ (h >>> 16), 0x45d9f3b) >>> 0;
  h = Math.imul(h ^ (h >>> 13), 0x45d9f3b) >>> 0;
  return ((h ^ (h >>> 16)) >>> 0) / 4294967296;
}

const colourOf = (name: string): number => PALETTE[name] ?? 0xff00ff;

function layerColour(p: Patron, slot: string): number | undefined {
  const layer = p.outfit.find((l) => l.slot === slot);
  if (!layer || layer.type === 'none') return undefined;
  return colourOf(layer.colour);
}

export function dollPlan(p: Patron, pose: DollPose): DollPlan {
  const { width, height } = DOLL_SIZES[pose];
  const skin = SKIN_TONES[Math.floor(mix(p.dollSeed, 1) * SKIN_TONES.length)]!;
  const build = 0.8 + mix(p.dollSeed, 2) * 0.4; // 0.8..1.2 body width factor
  const stage = drunkStage(p.intoxication);
  const swayPx = stage === 'loose' ? 1 : stage === 'messy' ? 2 : stage === 'maggot' ? 3 : 0;
  const rects: PlanRect[] = [];

  const seedForPhoto = pose === 'idPhoto' ? p.idCard.photoSeed : p.dollSeed;
  const photoSkin = SKIN_TONES[Math.floor(mix(seedForPhoto, 1) * SKIN_TONES.length)]!;
  const hair = layerColour(p, 'hair') ?? 0x14141c;
  const photoHair = pose === 'idPhoto' && p.idCard.photoSeed !== p.dollSeed
    ? (Object.values(PALETTE)[Math.floor(mix(seedForPhoto, 3) * 10)] ?? hair)
    : hair;

  if (pose === 'idPhoto') {
    // Head-and-shoulders on a flat card background.
    rects.push({ x: 0, y: 0, w: width, h: height, colour: 0xdad4c4 });
    rects.push({ x: 7, y: 6, w: 10, h: 10, colour: photoSkin });     // face
    rects.push({ x: 6, y: 3, w: 12, h: 4, colour: photoHair });      // hair
    rects.push({ x: 5, y: 16, w: 14, h: 8, colour: layerColour(p, 'top') ?? 0x555555 }); // shoulders
    return { width, height, rects, swayPx: 0 };
  }

  if (pose === 'floorTopDown') {
    // Seen from above: hair blob over shoulders (top colour).
    const top = layerColour(p, 'outer') ?? layerColour(p, 'top') ?? 0x555555;
    rects.push({ x: 3, y: 3, w: 10, h: 10, colour: top });   // shoulders
    rects.push({ x: 5, y: 5, w: 6, h: 6, colour: hair });    // head/hair
    return { width, height, rects, swayPx };
  }

  // queue pose — full front-facing figure, 32x48.
  const bw = Math.round(12 * build); // body width
  const cx = Math.floor(width / 2);

  const shoes = layerColour(p, 'shoes') ?? 0x14141c;
  const legs = layerColour(p, 'legs') ?? 0x333344;
  const top = layerColour(p, 'top') ?? 0x555566;
  const outer = layerColour(p, 'outer');
  const acc = p.outfit.find((l) => l.slot === 'accessory');

  rects.push({ x: cx - 4, y: 44, w: 3, h: 4, colour: shoes });                 // L shoe
  rects.push({ x: cx + 1, y: 44, w: 3, h: 4, colour: shoes });                 // R shoe
  rects.push({ x: cx - 4, y: 30, w: 8, h: 14, colour: legs });                 // legs
  rects.push({ x: cx - Math.floor(bw / 2), y: 16, w: bw, h: 14, colour: top }); // torso
  if (outer !== undefined) {
    rects.push({ x: cx - Math.floor(bw / 2) - 1, y: 16, w: 3, h: 13, colour: outer }); // jacket L
    rects.push({ x: cx + Math.floor(bw / 2) - 2, y: 16, w: 3, h: 13, colour: outer }); // jacket R
  }
  const topLayer = p.outfit.find((l) => l.slot === 'top');
  if (topLayer?.logo) rects.push({ x: cx - 2, y: 20, w: 4, h: 3, colour: 0xffffff });  // visible logo
  rects.push({ x: cx - 3, y: 6, w: 6, h: 9, colour: skin });                   // face
  rects.push({ x: cx - 4, y: 3, w: 8, h: 4, colour: hair });                   // hair
  const hairLayer = p.outfit.find((l) => l.slot === 'hair');
  if (hairLayer?.type === 'long') rects.push({ x: cx - 5, y: 6, w: 2, h: 8, colour: hair });
  if (hairLayer?.type === 'mullet') rects.push({ x: cx - 4, y: 12, w: 8, h: 3, colour: hair });

  if (acc && acc.type !== 'none') {
    const accColour = colourOf(acc.colour);
    if (acc.type === 'bucketHat' || acc.type === 'cap') rects.push({ x: cx - 5, y: 2, w: 10, h: 3, colour: accColour });
    if (acc.type === 'sunnies') rects.push({ x: cx - 3, y: 8, w: 6, h: 2, colour: 0x000000 });
    if (acc.type === 'bumbag') rects.push({ x: cx - 5, y: 28, w: 10, h: 3, colour: accColour });
    if (acc.type === 'chain') rects.push({ x: cx - 2, y: 16, w: 4, h: 1, colour: 0xf0d060 });
  }

  // drunk tells: bloodshot eye pixels from 'loose' up
  if (swayPx > 0) {
    rects.push({ x: cx - 2, y: 9, w: 1, h: 1, colour: 0xcc3333 });
    rects.push({ x: cx + 1, y: 9, w: 1, h: 1, colour: 0xcc3333 });
  }

  // The disguise (design §4.1): a grudged regular's third appearance wears a
  // fake moustache and a big pair of NOT-sunnies. Deliberately absent from the
  // idPhoto pose — his card still shows the clean-shaven bloke you knocked
  // back on Thursday. Same walk. Same name. "Different" man.
  if (p.flags.disguised === true) {
    rects.push({ x: cx - 2, y: 11, w: 5, h: 1, colour: 0x2a1c10 }); // moustache
    rects.push({ x: cx - 3, y: 8, w: 2, h: 2, colour: 0x101018 }); // glasses L
    rects.push({ x: cx + 1, y: 8, w: 2, h: 2, colour: 0x101018 }); // glasses R
    rects.push({ x: cx - 1, y: 8, w: 2, h: 1, colour: 0x101018 }); // bridge
  }

  return { width, height, rects, swayPx };
}
