// Shared primitives for the Garage Door Science animations.
// All elements are SVG-based schematics drawn on a charcoal stage.

const COLORS = {
  bg:        '#1C1C1A',  // shell-dark
  bgLight:   '#F5F4F1',  // off-white
  ink:       '#F5F4F1',
  inkDim:    'rgba(245,244,241,0.55)',
  inkFaint:  'rgba(245,244,241,0.25)',
  line:      'rgba(245,244,241,0.35)',
  grid:      'rgba(245,244,241,0.08)',
  orange:    '#D4541A',  // accent-primary (burnt orange)
  blue:      '#6FA3CC',  // calm steel blue (brighter variant for dark bg)
  steelBlue: '#1A5C8A',
  warm:      '#E8A87C',  // warm steel (hot)
  cold:      '#8AB6D6',  // cold steel
  danger:    '#E74C3C',
  warning:   '#D4851A',
  success:   '#5BA377',
};

const FONTS = {
  serif: "'DM Serif Display', Georgia, serif",
  sans:  "'Inter', system-ui, sans-serif",
  mono:  "'JetBrains Mono', ui-monospace, monospace",
};

// ── Atmospheric backdrop ─────────────────────────────────────────
// Subtle grid + vignette. Gives the schematic stage its "blueprint" feel.
function Backdrop({ tint = COLORS.bg, showGrid = true, gridColor = COLORS.grid }) {
  return (
    <div style={{ position: 'absolute', inset: 0, background: tint, overflow: 'hidden' }}>
      {showGrid && (
        <svg width="100%" height="100%" style={{ position: 'absolute', inset: 0 }}>
          <defs>
            <pattern id="grid" width="64" height="64" patternUnits="userSpaceOnUse">
              <path d="M 64 0 L 0 0 0 64" fill="none" stroke={gridColor} strokeWidth="1" />
            </pattern>
            <radialGradient id="vignette" cx="50%" cy="50%" r="75%">
              <stop offset="0%" stopColor="rgba(0,0,0,0)" />
              <stop offset="100%" stopColor="rgba(0,0,0,0.5)" />
            </radialGradient>
          </defs>
          <rect width="100%" height="100%" fill="url(#grid)" />
          <rect width="100%" height="100%" fill="url(#vignette)" />
        </svg>
      )}
    </div>
  );
}

// ── Label chip ──────────────────────────────────────────────
// JetBrains Mono tag. Used for callouts & captions.
function LabelChip({ text, x, y, color = COLORS.ink, accent = null, align = 'left' }) {
  const translate = align === 'center' ? 'translate(-50%, -50%)' : align === 'right' ? 'translate(-100%, -50%)' : 'translate(0, -50%)';
  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      transform: translate,
      fontFamily: FONTS.mono,
      fontSize: 14,
      fontWeight: 500,
      letterSpacing: '0.08em',
      textTransform: 'uppercase',
      color,
      display: 'flex',
      alignItems: 'center',
      gap: 8,
      whiteSpace: 'nowrap',
    }}>
      {accent && <span style={{
        width: 8, height: 8, background: accent,
        borderRadius: 1, display: 'inline-block',
      }}/>}
      {text}
    </div>
  );
}

// ── Caption / beat title ──────────────────────────────────────────────
// A headline + subhead, entering from the bottom.
function Caption({ headline, sub, x, y, maxWidth = 600, align = 'left' }) {
  const { localTime, duration } = useSprite();
  const exitStart = Math.max(0, duration - 0.5);
  let opacity = 1;
  let ty = 0;

  if (localTime < 0.5) {
    const t = Easing.easeOutCubic(clamp(localTime / 0.5, 0, 1));
    opacity = t;
    ty = (1 - t) * 20;
  } else if (localTime > exitStart) {
    const t = Easing.easeInCubic(clamp((localTime - exitStart) / 0.5, 0, 1));
    opacity = 1 - t;
    ty = -t * 8;
  }

  const textAlign = align;
  const translateX = align === 'center' ? '-50%' : align === 'right' ? '-100%' : '0';

  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      transform: `translate(${translateX}, ${ty}px)`,
      opacity,
      maxWidth,
      textAlign,
      willChange: 'transform, opacity',
    }}>
      {headline && (
        <div style={{
          fontFamily: FONTS.serif,
          fontSize: 56,
          lineHeight: 1.08,
          color: COLORS.ink,
          letterSpacing: '-0.01em',
          textWrap: 'pretty',
        }}>{headline}</div>
      )}
      {sub && (
        <div style={{
          marginTop: 14,
          fontFamily: FONTS.sans,
          fontSize: 20,
          lineHeight: 1.45,
          color: COLORS.inkDim,
          fontWeight: 400,
          textWrap: 'pretty',
        }}>{sub}</div>
      )}
    </div>
  );
}

// ── Number counter ──────────────────────────────────────────────
// Tallies from → to with easing; supports suffix (e.g., " lb").
function CountUp({ from, to, duration: dur = 1, x, y, size = 120, suffix = '', color = COLORS.orange, align = 'left' }) {
  const { localTime } = useSprite();
  const p = Easing.easeOutCubic(clamp(localTime / dur, 0, 1));
  const val = Math.round(from + (to - from) * p);
  const translateX = align === 'center' ? '-50%' : align === 'right' ? '-100%' : '0';
  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      transform: `translate(${translateX}, 0)`,
      fontFamily: FONTS.mono,
      fontSize: size,
      fontWeight: 500,
      color,
      fontVariantNumeric: 'tabular-nums',
      lineHeight: 1,
      letterSpacing: '-0.02em',
    }}>
      {val.toLocaleString()}<span style={{ color: COLORS.inkDim, fontSize: size * 0.4, marginLeft: 6 }}>{suffix}</span>
    </div>
  );
}

// ── Schematic garage door (cross-section / elevation) ──────────────────────────────────────────────
// Drawn in SVG. Has panels, a header (top rail), torsion tube with visible spring.
// lift: 0 = closed, 1 = fully open. Controls how high the door has slid up.
function DoorSchematic({ x, y, width = 400, height = 460, lift = 0, showSpring = true, springWind = 0, accentPanel = null }) {
  const panelCount = 4;
  const panelH = (height - 30) / panelCount; // 30 reserved for top rail + spring area
  const topRail = 30;

  // Open: door translates up by panelCount * panelH * lift, up to 1 panel worth bunched
  const lifted = lift * (panelCount - 0.2) * panelH;

  return (
    <svg width={width} height={height + 60} style={{ position: 'absolute', left: x, top: y, overflow: 'visible' }}>
      <defs>
        <linearGradient id="panel-grad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="#2A2A27"/>
          <stop offset="100%" stopColor="#1F1F1D"/>
        </linearGradient>
      </defs>

      {/* Floor */}
      <line x1="-20" y1={height + 30} x2={width + 20} y2={height + 30} stroke={COLORS.line} strokeWidth="1.5"/>
      {[...Array(6)].map((_, i) => (
        <line key={i}
          x1={-20 + i * ((width + 40) / 6)} y1={height + 38}
          x2={-30 + i * ((width + 40) / 6)} y2={height + 50}
          stroke={COLORS.inkFaint} strokeWidth="1"/>
      ))}

      {/* Track rails */}
      <line x1="4" y1={topRail} x2="4" y2={height + 30} stroke={COLORS.line} strokeWidth="1.5" strokeDasharray="2 4"/>
      <line x1={width - 4} y1={topRail} x2={width - 4} y2={height + 30} stroke={COLORS.line} strokeWidth="1.5" strokeDasharray="2 4"/>

      {/* Header / top frame */}
      <rect x="-10" y="0" width={width + 20} height={topRail} fill={COLORS.ink} opacity="0.1" stroke={COLORS.line} strokeWidth="1"/>

      {/* Torsion tube + spring */}
      {showSpring && (
        <g transform={`translate(0, ${topRail / 2})`}>
          {/* shaft */}
          <line x1="10" y1="0" x2={width - 10} y2="0" stroke={COLORS.inkDim} strokeWidth="2"/>
          {/* spring coils */}
          <g transform={`translate(${width * 0.25}, 0)`}>
            <TorsionSpringCoils width={width * 0.5} wind={springWind}/>
          </g>
          {/* end caps */}
          <circle cx="10" cy="0" r="4" fill={COLORS.ink}/>
          <circle cx={width - 10} cy="0" r="4" fill={COLORS.ink}/>
        </g>
      )}

      {/* Door panels (each is a rect, door as whole slides up) */}
      <g transform={`translate(0, ${-lifted})`}>
        {[...Array(panelCount)].map((_, i) => {
          const py = topRail + i * panelH;
          const isAccent = accentPanel === i;
          return (
            <g key={i}>
              <rect
                x="6" y={py}
                width={width - 12} height={panelH - 2}
                fill="url(#panel-grad)"
                stroke={isAccent ? COLORS.orange : COLORS.line}
                strokeWidth={isAccent ? 2 : 1}
              />
              {/* Panel detail line */}
              <line
                x1="14" y1={py + panelH * 0.5}
                x2={width - 14} y2={py + panelH * 0.5}
                stroke={COLORS.inkFaint} strokeWidth="0.5"
              />
              {/* Windows on top panel */}
              {i === 0 && (
                <g>
                  {[...Array(4)].map((_, w) => (
                    <rect key={w}
                      x={18 + w * ((width - 36) / 4)}
                      y={py + 8}
                      width={(width - 36) / 4 - 6}
                      height={panelH - 20}
                      fill="#3a4550"
                      opacity="0.4"
                      stroke={COLORS.inkFaint}
                      strokeWidth="0.5"
                    />
                  ))}
                </g>
              )}
              {/* Roller indicator */}
              <circle cx="4" cy={py + panelH / 2} r="3" fill={COLORS.line}/>
              <circle cx={width - 4} cy={py + panelH / 2} r="3" fill={COLORS.line}/>
            </g>
          );
        })}
      </g>
    </svg>
  );
}

// ── Torsion spring: helical coils drawn edge-on ─────────────────────────────
// Implemented as a series of vertical sine lobes. `wind` shifts the phase to
// suggest rotation without fully animating a 3D coil.
function TorsionSpringCoils({ width = 200, coils = 14, wind = 0, color = COLORS.ink, highlight = null }) {
  const step = width / coils;
  const lobes = [];
  for (let i = 0; i < coils; i++) {
    const x1 = i * step;
    const x2 = (i + 1) * step;
    // a single coil: d attr draws a small ellipse with phase offset
    // simulate rotation by shifting the dash pattern
    lobes.push(
      <g key={i}>
        <ellipse cx={(x1 + x2) / 2} cy={0} rx={step / 2 + 0.5} ry={7}
          fill="none"
          stroke={highlight != null && i === highlight ? COLORS.danger : color}
          strokeWidth={1.5}
          strokeDasharray="3 3"
          strokeDashoffset={-wind * step}
        />
      </g>
    );
  }
  return (
    <g>
      {/* Outer shell hint */}
      <line x1="0" y1="-8" x2={width} y2="-8" stroke={color} strokeWidth="0.5" opacity="0.3"/>
      <line x1="0" y1="8" x2={width} y2="8" stroke={color} strokeWidth="0.5" opacity="0.3"/>
      {lobes}
    </g>
  );
}

// ── Force arrow ──────────────────────────────────────────────
// SVG arrow with optional label. `dir` = 'down' | 'up' | 'left' | 'right'.
function ForceArrow({ x, y, length = 80, dir = 'down', color = COLORS.orange, label = null, labelSide = 'right', thickness = 3 }) {
  const { localTime, duration } = useSprite();
  const p = Easing.easeOutCubic(clamp(localTime / 0.4, 0, 1));
  const exitStart = Math.max(0, duration - 0.3);
  let opacity = p;
  if (localTime > exitStart) opacity = 1 - clamp((localTime - exitStart) / 0.3, 0, 1);

  const head = 12;
  let line, labelPos;
  switch (dir) {
    case 'down':
      line = <g><line x1="0" y1="0" x2="0" y2={length * p} stroke={color} strokeWidth={thickness}/><polygon points={`0,${length * p} -${head/2},${length * p - head} ${head/2},${length * p - head}`} fill={color} opacity={p > 0.9 ? 1 : 0}/></g>;
      labelPos = { x: labelSide === 'right' ? 14 : -14, y: length * 0.6, anchor: labelSide === 'right' ? 'start' : 'end' };
      break;
    case 'up':
      line = <g><line x1="0" y1="0" x2="0" y2={-length * p} stroke={color} strokeWidth={thickness}/><polygon points={`0,${-length * p} -${head/2},${-length * p + head} ${head/2},${-length * p + head}`} fill={color} opacity={p > 0.9 ? 1 : 0}/></g>;
      labelPos = { x: labelSide === 'right' ? 14 : -14, y: -length * 0.6, anchor: labelSide === 'right' ? 'start' : 'end' };
      break;
    case 'left':
      line = <g><line x1="0" y1="0" x2={-length * p} y2="0" stroke={color} strokeWidth={thickness}/><polygon points={`${-length * p},0 ${-length * p + head},${-head/2} ${-length * p + head},${head/2}`} fill={color} opacity={p > 0.9 ? 1 : 0}/></g>;
      labelPos = { x: -length * 0.5, y: -12, anchor: 'middle' };
      break;
    case 'right':
      line = <g><line x1="0" y1="0" x2={length * p} y2="0" stroke={color} strokeWidth={thickness}/><polygon points={`${length * p},0 ${length * p - head},${-head/2} ${length * p - head},${head/2}`} fill={color} opacity={p > 0.9 ? 1 : 0}/></g>;
      labelPos = { x: length * 0.5, y: -12, anchor: 'middle' };
      break;
  }

  return (
    <svg width="300" height="300" viewBox="-150 -150 300 300" style={{
      position: 'absolute', left: x - 150, top: y - 150, overflow: 'visible', opacity,
    }}>
      {line}
      {label && (
        <text x={labelPos.x} y={labelPos.y}
          fontFamily={FONTS.mono} fontSize="13" fontWeight="500"
          fill={color} textAnchor={labelPos.anchor}
          dominantBaseline="middle" style={{ letterSpacing: '0.04em', textTransform: 'uppercase' }}>
          {label}
        </text>
      )}
    </svg>
  );
}

// ── Thermometer ──────────────────────────────────────────────
// Classic bulb + tube thermometer, visualizes temperature from a props range.
function Thermometer({ x, y, temp, minTemp = 0, maxTemp = 100, height: thermH = 220, label = null, criticalTemp = null }) {
  const tubeW = 24;
  const bulbR = 22;
  const pct = clamp((temp - minTemp) / (maxTemp - minTemp), 0, 1);
  const fillColor = temp < 40 ? COLORS.cold : temp > 70 ? COLORS.warm : COLORS.ink;

  return (
    <svg width="120" height={thermH + 80} viewBox={`-60 0 120 ${thermH + 80}`} style={{
      position: 'absolute', left: x - 60, top: y,
    }}>
      {/* tube outline */}
      <rect x={-tubeW/2} y="10" width={tubeW} height={thermH - bulbR} rx={tubeW/2} ry={tubeW/2}
        fill={COLORS.bg} stroke={COLORS.line} strokeWidth="1.5"/>
      {/* mercury fill */}
      <rect x={-tubeW/2 + 4} y={10 + (thermH - bulbR - 4) * (1 - pct) + 2}
        width={tubeW - 8} height={(thermH - bulbR - 4) * pct}
        fill={fillColor} rx={(tubeW - 8)/2}/>
      {/* bulb */}
      <circle cx="0" cy={thermH} r={bulbR} fill={fillColor} stroke={COLORS.line} strokeWidth="1.5"/>

      {/* tick marks */}
      {[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
        <g key={i}>
          <line
            x1={tubeW/2 + 4} y1={10 + (thermH - bulbR) * (1 - t)}
            x2={tubeW/2 + 12} y2={10 + (thermH - bulbR) * (1 - t)}
            stroke={COLORS.line} strokeWidth="1"/>
          <text x={tubeW/2 + 18} y={10 + (thermH - bulbR) * (1 - t) + 4}
            fontFamily={FONTS.mono} fontSize="10" fill={COLORS.inkDim}>
            {Math.round(minTemp + t * (maxTemp - minTemp))}°
          </text>
        </g>
      ))}

      {/* critical line */}
      {criticalTemp != null && (() => {
        const cp = clamp((criticalTemp - minTemp) / (maxTemp - minTemp), 0, 1);
        const ty = 10 + (thermH - bulbR) * (1 - cp);
        return (
          <g>
            <line x1={-tubeW/2 - 20} y1={ty} x2={-tubeW/2 - 4} y2={ty} stroke={COLORS.danger} strokeWidth="1.5" strokeDasharray="3 2"/>
            <text x={-tubeW/2 - 24} y={ty + 4}
              fontFamily={FONTS.mono} fontSize="10" fill={COLORS.danger} textAnchor="end">
              {criticalTemp}°
            </text>
          </g>
        );
      })()}

      {/* current temp read-out */}
      <text x="0" y={thermH + bulbR + 24} textAnchor="middle"
        fontFamily={FONTS.mono} fontSize="20" fontWeight="500" fill={fillColor}>
        {Math.round(temp)}°F
      </text>
      {label && (
        <text x="0" y={thermH + bulbR + 44} textAnchor="middle"
          fontFamily={FONTS.mono} fontSize="10" fill={COLORS.inkDim}
          style={{ letterSpacing: '0.08em', textTransform: 'uppercase' }}>
          {label}
        </text>
      )}
    </svg>
  );
}

// ── Brand mark (real logo) ──────────────────────────────────────────────
function BrandMark({ x, y, align = 'left', scale = 1 }) {
  const translateX = align === 'center' ? '-50%' : align === 'right' ? '-100%' : '0';
  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      transform: `translate(${translateX}, 0)`,
      transformOrigin: align === 'center' ? 'center top' : align === 'right' ? 'right top' : 'left top',
    }}>
      <img
        src="assets/logo-full.webp"
        alt="Garage Door Science"
        style={{ width: 360 * scale, height: 'auto', display: 'block' }}
      />
    </div>
  );
}

// Export everything
Object.assign(window, {
  COLORS, FONTS,
  Backdrop, LabelChip, Caption, CountUp,
  DoorSchematic, TorsionSpringCoils, ForceArrow,
  Thermometer, BrandMark,
});
