// Isometric truck/pack renderer — pure SVG with painter's-algorithm sort.
// World coords: x → truck width (right), y → height (up), z → depth (away).
// Classic 30° iso: sx = (x-z)·cos30, sy = (x+z)·sin30 − y

(function () {
  function shade(hex, pct) {
    const n = parseInt(hex.slice(1), 16);
    let r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255;
    const f = pct / 100;
    r = Math.round(Math.max(0, Math.min(255, r + (f > 0 ? 255 - r : r) * f)));
    g = Math.round(Math.max(0, Math.min(255, g + (f > 0 ? 255 - g : g) * f)));
    b = Math.round(Math.max(0, Math.min(255, b + (f > 0 ? 255 - b : b) * f)));
    return '#' + ((r << 16) | (g << 8) | b).toString(16).padStart(6, '0');
  }

  const C30 = Math.cos(Math.PI / 6);
  const S30 = Math.sin(Math.PI / 6);

  function packLoad(load, catalog, truck) {
    const items = [];
    for (const row of load) {
      const c = catalog.find((x) => x.id === row.id);
      if (!c) continue;
      for (let i = 0; i < row.qty; i++) items.push({ ...c });
    }
    items.sort((a, b) => (b.h * b.w * b.d) - (a.h * a.w * a.d));

    const placed = [];
    let cursorZ = 0, rowMaxD = 0, rowCursorX = 0;
    for (const it of items) {
      if (rowCursorX + it.w > truck.w) {
        cursorZ += rowMaxD;
        rowCursorX = 0;
        rowMaxD = 0;
      }
      if (cursorZ + it.d > truck.d) break;
      placed.push({
        x: rowCursorX, y: 0, z: cursorZ,
        w: it.w, h: it.h, d: it.d,
        color: it.color, label: it.id, id: it.id,
      });
      rowCursorX += it.w;
      rowMaxD = Math.max(rowMaxD, it.d);
    }
    return placed;
  }

  function IsoTruck({ load, catalog, truck, scale = 0.4, highlight = null, theme = 'light', showLabels = true, showGrid = true }) {
    const placed = packLoad(load, catalog, truck);
    placed.sort((a, b) => (a.x + a.z) - (b.x + b.z) || (a.z - b.z));

    const proj = (x, y, z) => [ (x - z) * C30 * scale, ((x + z) * S30 - y) * scale ];

    const tcorners = [];
    for (const x of [0, truck.w]) for (const y of [0, truck.h]) for (const z of [0, truck.d]) tcorners.push(proj(x, y, z));
    const xs = tcorners.map((p) => p[0]);
    const ys = tcorners.map((p) => p[1]);
    const minX = Math.min(...xs), maxX = Math.max(...xs);
    const minY = Math.min(...ys), maxY = Math.max(...ys);
    const pad = 20;
    const W = maxX - minX + pad * 2;
    const H = maxY - minY + pad * 2;
    const ox = -minX + pad, oy = -minY + pad;
    const P = (x, y, z) => {
      const [sx, sy] = proj(x, y, z);
      return [sx + ox, sy + oy];
    };
    const poly = (pts) => pts.map((p) => P(...p).join(',')).join(' ');

    const isDark = theme === 'dark';
    const floorFill = isDark ? 'rgba(255,255,255,0.025)' : 'rgba(0,0,0,0.03)';
    const wallFill  = isDark ? 'rgba(255,255,255,0.045)' : 'rgba(0,0,0,0.05)';
    const wallStroke = isDark ? 'rgba(255,255,255,0.28)'  : 'rgba(0,0,0,0.32)';
    const gridStroke = isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)';

    const grid = [];
    for (let x = 0; x <= truck.w; x += 100) {
      const [x1, y1] = P(x, 0, 0);
      const [x2, y2] = P(x, 0, truck.d);
      grid.push(<line key={'gx'+x} x1={x1} y1={y1} x2={x2} y2={y2} stroke={gridStroke} strokeWidth={1} />);
    }
    for (let z = 0; z <= truck.d; z += 100) {
      const [x1, y1] = P(0, 0, z);
      const [x2, y2] = P(truck.w, 0, z);
      grid.push(<line key={'gz'+z} x1={x1} y1={y1} x2={x2} y2={y2} stroke={gridStroke} strokeWidth={1} />);
    }

    return (
      <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} xmlns="http://www.w3.org/2000/svg"
           shapeRendering="geometricPrecision" style={{ display: 'block', maxWidth: '100%', maxHeight: '100%' }}>
        <polygon points={poly([[0,0,0],[truck.w,0,0],[truck.w,truck.h,0],[0,truck.h,0]])}
          fill={wallFill} stroke={wallStroke} strokeWidth={1.5} strokeLinejoin="miter" />
        <polygon points={poly([[0,0,0],[0,0,truck.d],[0,truck.h,truck.d],[0,truck.h,0]])}
          fill={wallFill} stroke={wallStroke} strokeWidth={1.5} strokeLinejoin="miter" />
        <polygon points={poly([[0,0,0],[truck.w,0,0],[truck.w,0,truck.d],[0,0,truck.d]])}
          fill={floorFill} stroke={wallStroke} strokeWidth={1.5} strokeLinejoin="miter" />
        {showGrid && <g>{grid}</g>}

        {placed.map((b, i) => {
          const top   = shade(b.color, 22);
          const front = b.color;
          const right = shade(b.color, -28);
          const isHl  = highlight === b.id;
          const boxStroke = isHl ? '#fffd5b' : 'rgba(0,0,0,0.35)';
          const sw = isHl ? 2 : 0.8;

          const X0 = b.x, X1 = b.x + b.w;
          const Y0 = b.y, Y1 = b.y + b.h;
          const Z0 = b.z, Z1 = b.z + b.d;
          const [lx, ly] = P(X0 + b.w / 2, Y0 + Math.min(20, b.h * 0.18), Z1);
          const labelSize = Math.max(8, Math.min(11, Math.min(b.w, b.h) * 0.12 * scale * 2));

          return (
            <g key={i}>
              <polygon points={poly([[X1,Y0,Z0],[X1,Y0,Z1],[X1,Y1,Z1],[X1,Y1,Z0]])}
                fill={right} stroke={boxStroke} strokeWidth={sw} strokeLinejoin="miter" />
              <polygon points={poly([[X0,Y0,Z1],[X1,Y0,Z1],[X1,Y1,Z1],[X0,Y1,Z1]])}
                fill={front} stroke={boxStroke} strokeWidth={sw} strokeLinejoin="miter" />
              <polygon points={poly([[X0,Y1,Z0],[X1,Y1,Z0],[X1,Y1,Z1],[X0,Y1,Z1]])}
                fill={top} stroke={boxStroke} strokeWidth={sw} strokeLinejoin="miter" />
              {showLabels && b.label && b.w * scale > 22 && (
                <text x={lx} y={ly} fill="rgba(255,255,255,0.95)"
                  fontSize={labelSize} fontFamily="ui-monospace, 'IBM Plex Mono', monospace"
                  textAnchor="middle" fontWeight="700"
                  style={{ pointerEvents: 'none', letterSpacing: '0.5px' }}>
                  {b.label}
                </text>
              )}
            </g>
          );
        })}
      </svg>
    );
  }

  function IsoCase({ w, h, d, color, scale, theme = 'dark' }) {
    const proj = (x, y, z) => [ (x - z) * C30 * scale, ((x + z) * S30 - y) * scale ];
    const corners = [];
    for (const X of [0, w]) for (const Y of [0, h]) for (const Z of [0, d]) corners.push(proj(X, Y, Z));
    const xs = corners.map((p) => p[0]);
    const ys = corners.map((p) => p[1]);
    const minX = Math.min(...xs), maxX = Math.max(...xs);
    const minY = Math.min(...ys), maxY = Math.max(...ys);
    const pad = 4;
    const W = maxX - minX + pad * 2;
    const H = maxY - minY + pad * 2;
    const P = (x, y, z) => {
      const [sx, sy] = proj(x, y, z);
      return [sx - minX + pad, sy - minY + pad];
    };
    const poly = (pts) => pts.map((p) => P(...p).join(',')).join(' ');
    const top   = shade(color,  22);
    const front = color;
    const right = shade(color, -28);
    const st = theme === 'dark' ? 'rgba(0,0,0,0.45)' : 'rgba(0,0,0,0.30)';
    return (
      <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} xmlns="http://www.w3.org/2000/svg" shapeRendering="geometricPrecision" style={{display:'block'}}>
        <polygon points={poly([[w,0,0],[w,0,d],[w,h,d],[w,h,0]])} fill={right} stroke={st} strokeWidth={0.8} strokeLinejoin="miter" />
        <polygon points={poly([[0,0,d],[w,0,d],[w,h,d],[0,h,d]])} fill={front} stroke={st} strokeWidth={0.8} strokeLinejoin="miter" />
        <polygon points={poly([[0,h,0],[w,h,0],[w,h,d],[0,h,d]])} fill={top}   stroke={st} strokeWidth={0.8} strokeLinejoin="miter" />
      </svg>
    );
  }

  function floorFootprint(load, catalog, truck) {
    const placed = packLoad(load, catalog, truck);
    return placed.reduce((s, p) => s + p.w * p.d, 0);
  }

  Object.assign(window, { IsoTruck, IsoCase, packLoad, floorFootprint, isoShade: shade });
})();
