// b/ui.jsx — shared visual primitives for the Viewfinder prototype.

const T = {
  screen:  '#121210',   // camera black
  paper:   '#f7f5ee',   // warm sheet
  paper2:  '#efece2',
  ink:     '#1c1b16',
  ink2:    '#56534a',
  faint:   '#d9d5c8',
  line:    '#e3dfd3',
};
const mono = '"JetBrains Mono", ui-monospace, monospace';
const disp = '"Space Grotesk", system-ui, sans-serif';

// Seeded RNG so each model draws the same every render.
function seeded(str) {
  let h = 2166136261;
  for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 16777619); }
  return () => { h += 0x6D2B79F5; let t = h; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
}

function shade(hex, amt) {
  const n = parseInt(hex.slice(1), 16);
  let r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
  r = Math.max(0, Math.min(255, Math.round(r + amt)));
  g = Math.max(0, Math.min(255, Math.round(g + amt)));
  b = Math.max(0, Math.min(255, Math.round(b + amt)));
  return `rgb(${r},${g},${b})`;
}

// One studded brick drawn in flat 2.5-D at grid cell (gx,gy), w cells wide.
function Brick({ gx, gy, w = 2, color }) {
  const U = 16;             // unit
  const D = 7;              // depth of top face
  const h = U;              // brick height
  const x = gx * U, y = gy * U;
  const top = shade(color, 34), side = shade(color, -30), stroke = shade(color, -70);
  const studs = [];
  for (let i = 0; i < w; i++) {
    const cx = x + i * U + U / 2;
    studs.push(
      <g key={i}>
        <ellipse cx={cx} cy={y - D + 2} rx={4.6} ry={2.4} fill={top} stroke={stroke} strokeWidth="0.8" />
        <ellipse cx={cx} cy={y} rx={4.6} ry={2.4} fill={shade(color, 10)} stroke={stroke} strokeWidth="0.8" />
        <rect x={cx - 4.6} y={y - D + 2} width={9.2} height={D - 2} fill={shade(color, 18)} />
      </g>
    );
  }
  return (
    <g>
      {/* top face */}
      <polygon points={`${x},${y} ${x + D},${y - D} ${x + w * U + D},${y - D} ${x + w * U},${y}`} fill={top} stroke={stroke} strokeWidth="1" strokeLinejoin="round" />
      {/* front face */}
      <rect x={x} y={y} width={w * U} height={h} fill={color} stroke={stroke} strokeWidth="1" />
      {/* right face */}
      <polygon points={`${x + w * U},${y} ${x + w * U + D},${y - D} ${x + w * U + D},${y - D + h} ${x + w * U},${y + h}`} fill={side} stroke={stroke} strokeWidth="1" strokeLinejoin="round" />
      {studs}
    </g>
  );
}

// A generated brick-built silhouette from the project's palette.
function ModelArt({ project, size = 150 }) {
  const rnd = seeded(project.id);
  const pal = project.palette;
  // Build a stepped stack: rows from wide base up to a small top.
  const rows = [];
  let widthCells = 6;
  let gy = 7;
  for (let r = 0; r < 5 && widthCells >= 2; r++) {
    const startX = (8 - widthCells) / 2 + (rnd() < 0.5 ? 0 : 0.0);
    let gx = startX;
    const bricks = [];
    let remaining = widthCells;
    while (remaining > 0) {
      const w = remaining >= 3 && rnd() > 0.45 ? 2 : (remaining >= 2 && rnd() > 0.5 ? 2 : 1);
      const ww = Math.min(w, remaining);
      bricks.push({ gx, gy, w: ww, color: pal[Math.floor(rnd() * pal.length)] });
      gx += ww; remaining -= ww;
    }
    rows.push(bricks);
    gy -= 1;
    widthCells -= rnd() > 0.5 ? 1 : 2;
  }
  const all = rows.flat();
  return (
    <svg width={size} height={size} viewBox="0 0 144 144" style={{ display: 'block' }}>
      <defs>
        <radialGradient id={'g-' + project.id} cx="50%" cy="38%" r="65%">
          <stop offset="0%" stopColor="rgba(255,255,255,0.10)" />
          <stop offset="100%" stopColor="rgba(0,0,0,0)" />
        </radialGradient>
      </defs>
      <ellipse cx="72" cy="128" rx="48" ry="9" fill="rgba(0,0,0,0.10)" />
      <g transform="translate(28,18)">
        {all.map((b, i) => <Brick key={i} {...b} />)}
      </g>
      <rect x="0" y="0" width="144" height="144" fill={'url(#g-' + project.id + ')'} />
    </svg>
  );
}

// Real set photo when we have one (project.img), else the generated brick art.
function SetArt({ project, size = 150 }) {
  const [broken, setBroken] = React.useState(false);
  if (project.img && !broken) {
    return (
      <img src={project.img} alt={project.name} width={size} height={size}
        onError={() => setBroken(true)}
        style={{ width: size, height: size, objectFit: 'contain', display: 'block', mixBlendMode: 'multiply' }} />
    );
  }
  return <ModelArt project={project} size={size} />;
}

// ---- small components ------------------------------------------------------
function Chip({ children, accent, solid, style }) {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      fontFamily: mono, fontSize: 10.5, letterSpacing: 0.3,
      padding: '4px 9px', borderRadius: 999,
      border: '1px solid ' + (accent ? 'var(--accent)' : T.line),
      color: solid ? '#fff' : (accent ? 'var(--accent)' : T.ink2),
      background: solid ? 'var(--accent)' : (accent ? 'color-mix(in oklab, var(--accent) 12%, transparent)' : 'transparent'),
      whiteSpace: 'nowrap', ...style,
    }}>{children}</span>
  );
}

function Diff({ level, light }) {
  return (
    <span style={{ display: 'inline-flex', gap: 3, alignItems: 'center' }}>
      {[1,2,3,4,5].map(i => (
        <span key={i} style={{
          width: 7, height: 7, borderRadius: 7,
          border: '1px solid ' + (light ? 'rgba(255,255,255,.6)' : T.ink2),
          background: i <= level ? (light ? '#fff' : 'var(--accent)') : 'transparent',
        }} />
      ))}
    </span>
  );
}

function Btn({ children, onClick, kind = 'primary', style, disabled }) {
  const base = {
    fontFamily: disp, fontSize: 15, fontWeight: 600, letterSpacing: 0.2,
    border: 'none', borderRadius: 14, padding: '14px 18px', cursor: disabled ? 'default' : 'pointer',
    width: '100%', transition: 'transform .12s ease, filter .12s ease, background .2s',
    opacity: disabled ? 0.4 : 1,
  };
  const kinds = {
    primary: { background: 'var(--accent)', color: '#fff' },
    ghost: { background: 'transparent', color: T.ink, border: '1.5px solid ' + T.line },
    dark: { background: T.ink, color: T.paper },
  };
  return (
    <button onClick={disabled ? undefined : onClick}
      onMouseDown={e => !disabled && (e.currentTarget.style.transform = 'scale(0.975)')}
      onMouseUp={e => (e.currentTarget.style.transform = '')}
      onMouseLeave={e => (e.currentTarget.style.transform = '')}
      style={{ ...base, ...kinds[kind], ...style }}>{children}</button>
  );
}

// Color swatch with subtle stud.
function Swatch({ hex, size = 22 }) {
  return (
    <span style={{
      width: size, height: size * 0.62, borderRadius: 3, display: 'inline-block',
      background: hex, border: '1px solid rgba(0,0,0,.25)', position: 'relative',
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,.25)',
    }} />
  );
}

// Toast — fixed inside the phone screen.
function Toast({ msg }) {
  if (!msg) return null;
  return (
    <div style={{
      position: 'absolute', left: 16, right: 16, bottom: 92, zIndex: 60,
      background: T.ink, color: T.paper, fontFamily: mono, fontSize: 11.5,
      padding: '11px 14px', borderRadius: 12, textAlign: 'center',
      boxShadow: '0 10px 30px rgba(0,0,0,.35)',
      animation: 'toastIn .3s cubic-bezier(.2,.9,.3,1.2)',
    }}>{msg}</div>
  );
}

// Shared icon-button style (used by detail, build, sheets).
const iconBtn = {
  width: 38, height: 38, borderRadius: 19, background: 'rgba(247,245,238,.85)',
  border: '1px solid #e3dfd3', cursor: 'pointer', fontSize: 17, color: '#1c1b16',
  display: 'flex', alignItems: 'center', justifyContent: 'center',
};

Object.assign(window, { T, mono, disp, ModelArt, SetArt, Chip, Diff, Btn, Swatch, Toast, iconBtn });
