const { useState, useEffect, useRef, useMemo } = React;

function DSlider({ label, value, onChange, min, max, step, symbol, unit = '' }) {
  return (
    <label className="d-slider">
      <div className="d-slider-head">
        <span className="d-slider-sym">{symbol}</span>
        <span className="d-slider-lbl">{label}</span>
        <span className="d-slider-val">{value.toFixed(2)}{unit}</span>
      </div>
      <input
        type="range"
        min={min}
        max={max}
        step={step}
        value={value}
        onChange={(e) => onChange(parseFloat(e.target.value))}
        className="nat-slider"
      />
    </label>
  );
}

const MODES = ['lame', 'hugel', 'preston', 'narushin'];
const MODE_LABEL = {
  lame: 'Lamé',
  hugel: 'Hügelschäffer',
  preston: 'Preston',
  narushin: 'Narushin',
};
const MODE_YEAR = { lame: '1818', hugel: '1944', preston: '1953', narushin: '2021' };

function decodeState(s) {
  try {
    return JSON.parse(atob(s));
  } catch {
    return null;
  }
}

window.Designer = function Designer() {
  const initial = useMemo(() => {
    const hash = window.location.hash.replace(/^#/, '');
    const q = new URLSearchParams(hash);
    const egg = q.get('egg');
    if (egg) {
      const s = decodeState(egg);
      if (s) return s;
    }
    return null;
  }, []);

  const [mode, setMode] = useState(initial?.mode || 'narushin');
  const [params, setParams] = useState(
    initial?.params || { L: 2.0, B: 1.38, w: 0.18, p: 0.05, a: 1.0, b: 0.72, n: 2.5, c1: 0.15, c2: -0.04, c3: 0.0 }
  );
  const [shell, setShell] = useState(
    initial?.shell || { base: '#dbc896', accent: '#281b11', speckleCount: 0, speckleSize: 0.03, distribution: 'polar', variance: 0.6 }
  );
  const [seed, setSeed] = useState(initial?.seed || 7);
  const [species, setSpecies] = useState(initial?.species || 'chicken');
  const [spin, setSpin] = useState(false);
  const [compare, setCompare] = useState(false);
  const [orientation, setOrientation] = useState('vertical');

  const points = useMemo(() => pointsFor(mode, params), [mode, params]);
  const metrics = useMemo(() => window.eggMetrics(points), [points]);

  const allPoints = useMemo(() => {
    return {
      lame: pointsFor('lame', paramsFor('lame', params)),
      hugel: pointsFor('hugel', paramsFor('hugel', params)),
      preston: pointsFor('preston', paramsFor('preston', params)),
      narushin: pointsFor('narushin', paramsFor('narushin', params)),
    };
  }, [params]);

  const shellCfg = useMemo(() => {
    return {
      base: shell.base,
      accent: shell.accent,
      speckles: shell.speckleCount > 0
        ? {
            count: shell.speckleCount,
            sizeMin: shell.speckleSize * 0.4,
            sizeMax: shell.speckleSize,
            distribution: shell.distribution,
            variance: shell.variance,
          }
        : null,
    };
  }, [shell]);

  function applySpecies(key) {
    const s = window.SPECIES[key];
    if (!s) return;
    setSpecies(key);
    setMode('narushin');
    setParams((p) => ({ ...p, ...s.params }));
    if (s.shell.speckles) {
      setShell({
        base: s.shell.base,
        accent: s.shell.accent || '#281b11',
        speckleCount: s.shell.speckles.count,
        speckleSize: s.shell.speckles.sizeMax,
        distribution: s.shell.speckles.distribution,
        variance: s.shell.speckles.variance,
      });
    } else {
      setShell({
        base: s.shell.base,
        accent: '#281b11',
        speckleCount: 0,
        speckleSize: 0.03,
        distribution: 'polar',
        variance: 0.6,
      });
    }
    setSeed((x) => x + 1);
  }

  // Exported SVGs cannot depend on page CSS.
  function buildExportSVG() {
    const host = document.getElementById('designer-main-egg');
    if (!host) return null;
    const svgEl = host.querySelector('.d-egg-side svg') || host.querySelector('svg');
    if (!svgEl) return null;

    const clone = svgEl.cloneNode(true);
    clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    clone.removeAttribute('style');

    const vbAttr = clone.getAttribute('viewBox') || `0 0 ${clone.getAttribute('width') || 420} ${clone.getAttribute('height') || 320}`;
    const [vx, vy, vw, vh] = vbAttr.split(/\s+/).map(parseFloat);

    let outW = vw;
    let outH = vh;

    if (orientation === 'vertical') {
      const cx = vx + vw / 2;
      const cy = vy + vh / 2;
      const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
      g.setAttribute('transform', `rotate(-90 ${cx} ${cy})`);
      while (clone.firstChild) {
        g.appendChild(clone.firstChild);
      }
      clone.appendChild(g);
      outW = vh;
      outH = vw;
      clone.setAttribute('viewBox', `${cx - vh / 2} ${cy - vw / 2} ${vh} ${vw}`);
      clone.setAttribute('width', vh);
      clone.setAttribute('height', vw);
    }

    const inkVal = (getComputedStyle(document.body).getPropertyValue('--ink') || '').trim() || '#1a1713';
    let markup = new XMLSerializer().serializeToString(clone);
    markup = markup.replace(/var\(--ink\)/g, inkVal);
    return { markup, width: outW, height: outH };
  }

  function triggerDownload(blob, filename) {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  }

  function downloadSVG() {
    const out = buildExportSVG();
    if (!out) return;
    const blob = new Blob([out.markup], { type: 'image/svg+xml;charset=utf-8' });
    triggerDownload(blob, `egg-${mode}-${Date.now()}.svg`);
  }

  function downloadPNG(bg) {
    const out = buildExportSVG();
    if (!out) return;
    const scale = 4;
    const canvas = document.createElement('canvas');
    canvas.width = Math.round(out.width * scale);
    canvas.height = Math.round(out.height * scale);
    const ctx = canvas.getContext('2d');
    if (bg === 'white') {
      ctx.fillStyle = '#ffffff';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }
    const svgBlob = new Blob([out.markup], { type: 'image/svg+xml;charset=utf-8' });
    const url = URL.createObjectURL(svgBlob);
    const img = new Image();
    img.onload = () => {
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      URL.revokeObjectURL(url);
      canvas.toBlob((pngBlob) => {
        if (!pngBlob) return;
        triggerDownload(pngBlob, `egg-${mode}-${bg}-${Date.now()}.png`);
      }, 'image/png');
    };
    img.onerror = (e) => {
      URL.revokeObjectURL(url);
      console.error('PNG export failed', e);
    };
    img.src = url;
  }

  return (
    <div className="designer">
      <div className="designer-inner">
        <div className="d-stage">
          <div className="d-stage-chrome">
            <span className="d-stage-tag">plate v.</span>
            <span className="d-stage-name">
              your egg<span className="d-stage-dot">·</span>
              <em>{MODE_LABEL[mode]} form, {MODE_YEAR[mode]}</em>
            </span>
          </div>

          {compare ? (
            <CompareGrid points={allPoints} shell={shellCfg} seed={seed} orientation={orientation} />
          ) : (
            <div className="d-stage-egg" id="designer-main-egg">
              <div className={`d-egg-pair ${spin ? 'spinning' : ''}`}>
                <div className={`d-egg-side d-egg-side--${orientation}`}>
                  <window.EggSVG
                    points={points}
                    width={420}
                    height={320}
                    shell={shellCfg}
                    speckleSeed={seed}
                    inkColor="var(--ink)"
                    rotate={orientation === 'vertical' ? -90 : 0}
                  />
                  <div className="d-egg-cap">side view — ×1.0</div>
                </div>
                <div className="d-egg-end">
                  <EndView points={points} shell={shellCfg} seed={seed} spin={spin} />
                  <div className="d-egg-cap">end view{spin ? ' — rotating' : ''}</div>
                </div>
              </div>
            </div>
          )}

          <div className="d-metrics">
            <Metric label="length" value={metrics.L.toFixed(2)} unit="u" />
            <Metric label="breadth" value={metrics.B.toFixed(2)} unit="u" />
            <Metric label="elongation" value={metrics.elongation.toFixed(3)} unit="" />
            <Metric label="asymmetry" value={metrics.asymmetry.toFixed(3)} unit="" />
            <Metric label="volume" value={metrics.V.toFixed(3)} unit="u³" />
            <Metric label="surface" value={metrics.A.toFixed(3)} unit="u²" />
          </div>
        </div>

        <div className="d-rail">
            <div className="d-section">
            <div className="d-section-head">
              <span>i.</span>formula
            </div>
            <div className="d-mode-picker">
              {MODES.map((m) => (
                <button
                  key={m}
                  className={`d-mode-btn ${mode === m ? 'is-active' : ''}`}
                  onClick={() => setMode(m)}
                >
                  <span className="d-mode-yr">{MODE_YEAR[m]}</span>
                  <span className="d-mode-nm">{MODE_LABEL[m]}</span>
                </button>
              ))}
            </div>
          </div>

          <div className="d-section">
            <div className="d-section-head">
              <span>ii.</span>parameters
            </div>
            <div className="d-controls">
              {mode === 'lame' && (
                <>
                  <DSlider label="half-length" symbol="a" value={params.a} onChange={(v) => setParams((p) => ({ ...p, a: v }))} min={0.6} max={1.2} step={0.02} />
                  <DSlider label="half-width" symbol="b" value={params.b} onChange={(v) => setParams((p) => ({ ...p, b: v }))} min={0.4} max={1.0} step={0.02} />
                  <DSlider label="exponent" symbol="n" value={params.n} onChange={(v) => setParams((p) => ({ ...p, n: v }))} min={1.6} max={6} step={0.05} />
                </>
              )}
              {mode === 'hugel' && (
                <>
                  <DSlider label="half-length" symbol="a" value={params.a} onChange={(v) => setParams((p) => ({ ...p, a: v }))} min={0.7} max={1.2} step={0.02} />
                  <DSlider label="half-width" symbol="b" value={params.b} onChange={(v) => setParams((p) => ({ ...p, b: v }))} min={0.4} max={0.9} step={0.02} />
                  <DSlider label="offset" symbol="w" value={params.w} onChange={(v) => setParams((p) => ({ ...p, w: v }))} min={0} max={0.4} step={0.01} />
                </>
              )}
              {mode === 'preston' && (
                <>
                  <DSlider label="asymmetry" symbol="c₁" value={params.c1} onChange={(v) => setParams((p) => ({ ...p, c1: v }))} min={-0.3} max={0.4} step={0.01} />
                  <DSlider label="bicone" symbol="c₂" value={params.c2} onChange={(v) => setParams((p) => ({ ...p, c2: v }))} min={-0.25} max={0.25} step={0.01} />
                  <DSlider label="fine taper" symbol="c₃" value={params.c3} onChange={(v) => setParams((p) => ({ ...p, c3: v }))} min={-0.2} max={0.2} step={0.01} />
                </>
              )}
              {mode === 'narushin' && (
                <>
                  <DSlider label="length" symbol="L" value={params.L} onChange={(v) => setParams((p) => ({ ...p, L: v }))} min={1.3} max={2.5} step={0.02} />
                  <DSlider label="breadth" symbol="B" value={params.B} onChange={(v) => setParams((p) => ({ ...p, B: v }))} min={0.9} max={1.9} step={0.02} />
                  <DSlider label="offset" symbol="w" value={params.w} onChange={(v) => setParams((p) => ({ ...p, w: v }))} min={0} max={0.35} step={0.01} />
                  <DSlider label="pyriformity" symbol="D¹ᐟ₄" value={params.p} onChange={(v) => setParams((p) => ({ ...p, p: v }))} min={0} max={0.55} step={0.01} />
                </>
              )}
            </div>
          </div>

          <div className="d-section">
            <div className="d-section-head">
              <span>iii.</span>shell
            </div>
            <div className="d-swatches">
              {['#f0ebd6', '#e9c591', '#c8b593', '#a9d3d0', '#89b8bd', '#c8d3b5', '#4a5942', '#d6c896', '#e6d9a8', '#b8c19a', '#ede0c6', '#c3a57a'].map((c) => (
                <button
                  key={c}
                  className={`d-sw ${shell.base === c ? 'is-active' : ''}`}
                  style={{ background: c }}
                  onClick={() => setShell((s) => ({ ...s, base: c }))}
                  title={c}
                />
              ))}
            </div>
            <DSlider label="spot density" symbol="ρ" value={shell.speckleCount} onChange={(v) => setShell((s) => ({ ...s, speckleCount: Math.round(v) }))} min={0} max={220} step={1} />
            <DSlider label="spot size" symbol="s" value={shell.speckleSize} onChange={(v) => setShell((s) => ({ ...s, speckleSize: v }))} min={0.008} max={0.07} step={0.002} />
            <div className="d-distrib">
              {['uniform', 'polar', 'belt'].map((d) => (
                <button
                  key={d}
                  className={`d-dist-btn ${shell.distribution === d ? 'is-active' : ''}`}
                  onClick={() => setShell((s) => ({ ...s, distribution: d }))}
                >{d}</button>
              ))}
            </div>
            <button className="d-reseed" onClick={() => setSeed((x) => x + 1)}>⟲ re-speckle</button>
          </div>

          <div className="d-section">
            <div className="d-section-head">
              <span>iv.</span>species cabinet
            </div>
            <div className="d-species-grid">
              {Object.entries(window.SPECIES).map(([k, s]) => (
                <button
                  key={k}
                  className={`d-sp ${species === k ? 'is-active' : ''}`}
                  onClick={() => applySpecies(k)}
                  title={s.note}
                >
                  <SpeciesThumb preset={s} seed={7} />
                  <div className="d-sp-lbl">{s.label}</div>
                  <div className="d-sp-sci">{s.sci}</div>
                </button>
              ))}
            </div>
          </div>

          <div className="d-section">
            <div className="d-section-head">
              <span>v.</span>view &amp; export
            </div>
            <div className="d-actions">
              <button className="d-act" onClick={() => setSpin((s) => !s)}>
                {spin ? '■ stop' : '↻ spin'}
              </button>
              <button className={`d-act ${compare ? 'is-active' : ''}`} onClick={() => setCompare((c) => !c)}>
                {compare ? '← single' : '▦ compare formulas'}
              </button>
              <button
                className="d-act"
                onClick={() => setOrientation((o) => (o === 'vertical' ? 'horizontal' : 'vertical'))}
                title="toggle orientation"
              >
                {orientation === 'vertical' ? '↕ vertical' : '↔ horizontal'}
              </button>
              <button className="d-act" onClick={downloadSVG}>↓ SVG</button>
              <button className="d-act" onClick={() => downloadPNG('transparent')}>↓ PNG · transparent</button>
              <button className="d-act" onClick={() => downloadPNG('white')}>↓ PNG · white</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

function pointsFor(mode, params) {
  if (mode === 'lame') return window.lameEgg(params);
  if (mode === 'hugel') return window.hugelschafferEgg(params);
  if (mode === 'preston') return window.prestonEgg(params);
  if (mode === 'narushin') return window.narushinEgg(params);
  return [];
}

function paramsFor(mode, p) {
  if (mode === 'lame') return { a: (p.L || 2) / 2, b: (p.B || 1.38) / 2, n: 2.6 };
  if (mode === 'hugel') return { a: (p.L || 2) / 2, b: (p.B || 1.38) / 2, w: p.w ?? 0.15 };
  if (mode === 'preston') return { a: 1, b: ((p.B || 1.38) / (p.L || 2)), c1: (p.w ?? 0.15) * 0.8, c2: -0.05, c3: 0 };
  if (mode === 'narushin') return { L: p.L || 2, B: p.B || 1.38, w: p.w ?? 0.15, p: p.p ?? 0 };
  return {};
}

function Metric({ label, value, unit }) {
  return (
    <div className="d-metric">
      <div className="d-metric-lbl">{label}</div>
      <div className="d-metric-val">{value}<span>{unit}</span></div>
    </div>
  );
}

function EndView({ points, shell, seed, spin }) {
  const b = window.eggBounds(points);
  const maxY = Math.max(Math.abs(b.minY), Math.abs(b.maxY));
  const r = maxY;
  const circle = [];
  const steps = 120;
  for (let i = 0; i <= steps; i++) {
    const t = (i / steps) * Math.PI * 2;
    circle.push({ x: r * Math.cos(t), y: r * Math.sin(t) });
  }
  return (
    <div className={`end-view ${spin ? 'end-spin' : ''}`}>
      <window.EggSVG
        points={circle}
        width={220}
        height={220}
        shell={shell}
        speckleSeed={seed + 31}
        inkColor="var(--ink)"
      />
    </div>
  );
}

function CompareGrid({ points, shell, seed, orientation = 'horizontal' }) {
  return (
    <div className="d-compare">
      {MODES.map((m) => (
        <div key={m} className="d-compare-cell">
          <div className="d-compare-head">
            <span className="d-compare-yr">{MODE_YEAR[m]}</span>
            <span className="d-compare-nm">{MODE_LABEL[m]}</span>
          </div>
          <window.EggSVG
            points={points[m]}
            width={260}
            height={220}
            shell={shell}
            speckleSeed={seed}
            inkColor="var(--ink)"
            rotate={orientation === 'vertical' ? -90 : 0}
          />
        </div>
      ))}
    </div>
  );
}

function SpeciesThumb({ preset, seed = 7 }) {
  const pts = window.narushinEgg(preset.params);
  const shellCfg = {
    base: preset.shell.base,
    accent: preset.shell.accent,
    speckles: preset.shell.speckles,
  };
  return (
    <window.EggSVG
      points={pts}
      width={64}
      height={54}
      shell={shellCfg}
      speckleSeed={seed}
      padding={4}
      inkColor="var(--ink)"
    />
  );
}
