// Planet viewer: read-only display of a sealed planet. Shows 3D render,
// ID, spec, share URL, and "fork this planet" action.

const { useState: useStateV, useEffect: useEffectV, useRef: useRefV, useMemo: useMemoV } = React;

function PlanetClock({ rotationHours }) {
  const [tick, setTick] = useStateV(0);
  useEffectV(() => {
    const id = setInterval(() => setTick(t => t+1), 500);
    return () => clearInterval(id);
  }, []);
  // 1 real minute = 1 planet day. So planet hour = real second × (24/60) × (24/rotationHours)
  // Simpler: planet has a day that takes 60s scaled by rotationHours/24
  const now = Date.now()/1000; // seconds
  const dayLengthReal = 60 * ((rotationHours||24)/24);
  const planetT = (now % dayLengthReal) / dayLengthReal; // 0..1 of a day
  const hour = Math.floor(planetT * (rotationHours||24));
  const min = Math.floor((planetT * (rotationHours||24) - hour) * 60);
  return (
    <div className="planet-clock">
      <div className="clock-lbl">Local time</div>
      <div className="clock-val">{String(hour).padStart(2,'0')}:{String(min).padStart(2,'0')}</div>
      <div className="clock-sub">day is {rotationHours}h · 60s real</div>
    </div>
  );
}

function ViewerPanel({ planet, permalink, starfieldDensity, animIntensity }) {
  const canvasRef = useRefV(null);
  const sceneRef = useRefV(null);
  const [zoomLevel, setZoomLevel] = useStateV(1);
  const [exportBusy, setExportBusy] = useStateV(false);

  useEffectV(() => {
    if (!canvasRef.current) return;
    const scene = window.PLANETALIN.createPlanetScene(canvasRef.current, {
      cameraDistance: 3.6,
      starfieldDensity,
      animIntensity,
    });
    sceneRef.current = scene;
    scene.setSpec(planet);

    // Wheel zoom
    const canvas = canvasRef.current;
    function onWheel(e) {
      e.preventDefault();
      const delta = e.deltaY > 0 ? 0.9 : 1.1;
      scene.zoomBy(delta);
      setZoomLevel(scene.getZoom());
    }
    canvas.addEventListener('wheel', onWheel, { passive: false });

    // Pinch zoom (touch)
    let pinchStartDist = 0;
    let pinchStartZoom = 1;
    function dist(t1, t2) {
      const dx = t1.clientX - t2.clientX, dy = t1.clientY - t2.clientY;
      return Math.hypot(dx, dy);
    }
    function onTouchStart(e) {
      if (e.touches.length === 2) {
        pinchStartDist = dist(e.touches[0], e.touches[1]);
        pinchStartZoom = scene.getZoom();
      }
    }
    function onTouchMove(e) {
      if (e.touches.length === 2 && pinchStartDist > 0) {
        e.preventDefault();
        const d = dist(e.touches[0], e.touches[1]);
        scene.setZoom(pinchStartZoom * (d / pinchStartDist));
        setZoomLevel(scene.getZoom());
      }
    }
    canvas.addEventListener('touchstart', onTouchStart, { passive: false });
    canvas.addEventListener('touchmove', onTouchMove, { passive: false });

    return () => {
      canvas.removeEventListener('wheel', onWheel);
      canvas.removeEventListener('touchstart', onTouchStart);
      canvas.removeEventListener('touchmove', onTouchMove);
      scene.dispose();
    };
  }, [planet.id]);

  useEffectV(() => {
    if (sceneRef.current) {
      sceneRef.current.update({ starfieldDensity, animIntensity });
    }
  }, [starfieldDensity, animIntensity]);

  function zoomIn() { if (!sceneRef.current) return; sceneRef.current.zoomBy(1.2); setZoomLevel(sceneRef.current.getZoom()); }
  function zoomOut() { if (!sceneRef.current) return; sceneRef.current.zoomBy(1/1.2); setZoomLevel(sceneRef.current.getZoom()); }
  function zoomReset() { if (!sceneRef.current) return; sceneRef.current.setZoom(1); setZoomLevel(1); }

  const star = (window.PLANETALIN_STARS||[]).find(s => s.id === planet.star);
  const shareUrl = useMemoV(() => window.PLANETALIN.permalinkFor(planet), [planet]);
  const [copied, setCopied] = useStateV(false);
  function copyUrl() {
    navigator.clipboard?.writeText(shareUrl).catch(()=>{});
    setCopied(true);
    setTimeout(() => setCopied(false), 1400);
  }

  async function exportPng() {
    if (!sceneRef.current) return;
    setExportBusy(true);
    try {
      // Render a large version of the planet card (1200×1500) as a poster.
      const card = await renderPlanetCard(planet, sceneRef.current);
      const a = document.createElement('a');
      a.href = card;
      a.download = `planetalin-${planet.name || planet.id}.png`;
      a.click();
    } finally {
      setExportBusy(false);
    }
  }

  function exportJson() {
    const blob = new Blob([JSON.stringify(planet, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `planetalin-${planet.name || planet.id}.json`;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  }

  function saveToMyCatalogue() {
    const id = window.PLANETALIN.importCataloguePlanet(planet);
    if (id) window.PLANETALIN.go(`planet/${id}`);
  }

  return (
    <div className="viewer">
      <div className="viewer-stage">
        <canvas ref={canvasRef} className="planet-canvas" />
        <div className="stage-overlay">
          <div className="overlay-id">
            <div className="id-label">CATALOGUE</div>
            <div className="id-val">{planet.id}</div>
          </div>
          <div className="overlay-sealed">
            <div className="sealed-ring">
              <svg viewBox="0 0 100 100" className="sealed-svg">
                <defs>
                  <path id={`curve-${planet.id}`} d="M 50,50 m -34,0 a 34,34 0 1,1 68,0 a 34,34 0 1,1 -68,0" />
                </defs>
                <text className="sealed-text">
                  <textPath href={`#curve-${planet.id}`} startOffset="0">SEALED · PERMANENT · SEALED · PERMANENT · </textPath>
                </text>
              </svg>
              <div className="sealed-dot" />
            </div>
          </div>
          <PlanetClock rotationHours={planet.rotationHours} />
        </div>

        {/* Zoom controls */}
        <div className="zoom-controls" role="group" aria-label="Zoom">
          <button className="zoom-btn" onClick={zoomIn} aria-label="Zoom in" title="Zoom in">
            <svg viewBox="0 0 16 16"><path d="M8 3v10M3 8h10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" fill="none"/></svg>
          </button>
          <div className="zoom-readout">{Math.round(zoomLevel * 100)}%</div>
          <button className="zoom-btn" onClick={zoomOut} aria-label="Zoom out" title="Zoom out">
            <svg viewBox="0 0 16 16"><path d="M3 8h10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" fill="none"/></svg>
          </button>
          <button className="zoom-btn zoom-reset" onClick={zoomReset} aria-label="Reset zoom" title="Reset" disabled={Math.abs(zoomLevel - 1) < 0.01}>
            <svg viewBox="0 0 16 16"><path d="M3 8a5 5 0 1 0 1.5-3.5M3 3v3h3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" fill="none"/></svg>
          </button>
        </div>
      </div>

      <aside className="viewer-panel">
        <div className="v-eyebrow">Discovered {new Date(planet.createdAt || Date.now()).toLocaleDateString()}</div>
        <h1 className="v-name" style={{fontFamily:'"Cormorant Garamond", serif'}}>{planet.name}</h1>
        <div className="v-disc">by {planet.discoverer}</div>

        {planet.seal && (
          <div className="v-seal-row" title={`Creator seal · ${planet.seal.id}`}>
            <div className="v-seal-glyph" style={{background: `oklch(55% 0.18 ${planet.seal.hue})`, boxShadow: `0 0 16px oklch(55% 0.18 ${planet.seal.hue} / 0.55)`}}>{planet.seal.glyph}</div>
            <div className="v-seal-meta">
              <div className="v-seal-lbl">shaped by</div>
              <div className="v-seal-name" style={{fontFamily:'"Cormorant Garamond", serif'}}>{planet.seal.name}</div>
            </div>
            <div className="v-seal-id">{planet.seal.id}</div>
          </div>
        )}

        <section className="v-section">
          <div className="v-lbl">Atmosphere</div>
          <div className="v-atmos-bar">
            {planet.atmosphere.map(a => {
              const el = window.PLANETALIN_ELEMENTS.find(e => e.sym === a.sym);
              return <div key={a.sym} className="v-atmos-seg" style={{flex: a.pct, background: el?.color}} title={`${a.sym} ${a.pct}%`}>{a.sym}</div>;
            })}
          </div>
          <div className="v-atmos-list">
            {planet.atmosphere.map(a => {
              const el = window.PLANETALIN_ELEMENTS.find(e => e.sym === a.sym);
              return <div key={a.sym} className="v-atmos-row"><span className="dot" style={{background:el?.color}} /> {el?.name} · {a.pct}%</div>;
            })}
          </div>
        </section>

        <section className="v-grid">
          <div><div className="v-lbl">Surface</div><div className="v-val">{planet.surface}</div></div>
          <div><div className="v-lbl">Weather</div><div className="v-val">{planet.weather}</div></div>
          <div><div className="v-lbl">Life</div><div className="v-val">{planet.life}</div></div>
          <div><div className="v-lbl">Star</div><div className="v-val">{star?.name}</div></div>
          <div><div className="v-lbl">Orbit</div><div className="v-val">{planet.orbitAU?.toFixed(2)} AU</div></div>
          <div><div className="v-lbl">Day</div><div className="v-val">{planet.rotationHours} h{planet.retrograde ? ' ⟲' : ''}</div></div>
          <div><div className="v-lbl">Tilt</div><div className="v-val">{planet.axial}°</div></div>
          <div><div className="v-lbl">Moons</div><div className="v-val">{planet.moons.length}</div></div>
        </section>

        {(planet.mass != null || planet.radius != null || planet.tempK != null || planet.albedo != null || planet.realness != null) && (
          <section className="v-section v-physics">
            <div className="v-lbl">
              Physical parameters
              {planet.realness != null && (
                <span className="v-real-badge" title="How closely the render matches a real photograph of this body">
                  realness {Math.round(planet.realness * 100)}%
                </span>
              )}
            </div>
            <div className="v-grid v-grid-phys">
              {planet.mass != null        && <div><div className="v-lbl">Mass</div><div className="v-val">{planet.mass} M⊕</div></div>}
              {planet.radius != null      && <div><div className="v-lbl">Radius</div><div className="v-val">{planet.radius} R⊕</div></div>}
              {planet.tempK != null       && <div><div className="v-lbl">Temp</div><div className="v-val">{Math.round(planet.tempK - 273.15)}°C</div></div>}
              {planet.albedo != null      && <div><div className="v-lbl">Albedo</div><div className="v-val">{planet.albedo}</div></div>}
              {planet.eccentricity != null && <div><div className="v-lbl">Eccentricity</div><div className="v-val">{planet.eccentricity.toFixed(3)}</div></div>}
              {planet.oblateness != null && planet.oblateness > 0 && <div><div className="v-lbl">Oblateness</div><div className="v-val">{(planet.oblateness*100).toFixed(2)}%</div></div>}
              {planet.tidalLocked        && <div><div className="v-lbl">Rotation</div><div className="v-val">tidal-locked</div></div>}
            </div>
          </section>
        )}

        {planet.moons.length > 0 && (
          <section className="v-section">
            <div className="v-lbl">Satellites</div>
            <div className="v-moons">
              {planet.moons.map((m,i) => (
                <div key={i} className="v-moon">
                  <div className="v-moon-orb" style={{background:m.tint, width: 8+m.size*40+'px', height: 8+m.size*40+'px'}} />
                  <span>{m.name}</span>
                </div>
              ))}
            </div>
          </section>
        )}

        {planet.rings?.enabled && (
          <section className="v-section">
            <div className="v-lbl">Rings</div>
            <div className="v-val">{planet.rings.count} bands · {planet.rings.tilt}° tilt · {Math.round((planet.rings.density||0.5)*100)}% density</div>
          </section>
        )}

        <section className="v-share">
          <div className="v-lbl">Permalink</div>
          <div className="share-url" onClick={copyUrl}>
            <span className="share-text">{shareUrl}</span>
            <button className="share-btn">{copied ? 'copied ✓' : 'copy'}</button>
          </div>
          <div className="share-hint">Encodes the full spec. Works even for viewers whose browser has never seen this world.</div>
        </section>

        <section className="v-export">
          <button className="ghost-btn small" onClick={exportPng} disabled={exportBusy}>
            {exportBusy ? 'rendering…' : '↓ Poster PNG'}
          </button>
          <button className="ghost-btn small" onClick={exportJson}>↓ Spec JSON</button>
        </section>

        <div className="v-actions">
          {permalink ? (
            <>
              <button className="ghost-btn" onClick={() => window.PLANETALIN.go('')}>← Home</button>
              <button className="solid-btn" onClick={saveToMyCatalogue}>Add to my catalogue</button>
            </>
          ) : (
            <>
              <button className="ghost-btn" onClick={() => window.PLANETALIN.go('constellation')}>← Constellation</button>
              <button className="solid-btn" onClick={() => window.PLANETALIN.go(`create/${planet.id}`)}>Fork this world</button>
            </>
          )}
        </div>
      </aside>
    </div>
  );
}

window.PLANETALINViewer = { ViewerPanel };

// --- Poster PNG renderer --------------------------------------------------
// Composes a shareable 1200×1500 card: big 3D render on top, metadata below.
// Called from the viewer's "Poster PNG" button.
async function renderPlanetCard(planet, scene) {
  const W = 1200, H = 1500;
  const canvas = document.createElement('canvas');
  canvas.width = W;
  canvas.height = H;
  const ctx = canvas.getContext('2d');

  // Background — deep violet, matches the product ground
  const bg = ctx.createLinearGradient(0, 0, 0, H);
  bg.addColorStop(0, '#0f0822');
  bg.addColorStop(1, '#1a0e38');
  ctx.fillStyle = bg;
  ctx.fillRect(0, 0, W, H);

  // Subtle starfield
  for (let i = 0; i < 160; i++) {
    const x = Math.random() * W;
    const y = Math.random() * H;
    const r = Math.random() * 1.3;
    ctx.fillStyle = `rgba(255,255,255,${0.2 + Math.random()*0.6})`;
    ctx.beginPath();
    ctx.arc(x, y, r, 0, Math.PI*2);
    ctx.fill();
  }

  // Render the planet at 1000×1000 into the scene, then drop it into the card
  const planetImg = new Image();
  const planetDataUrl = scene.capture(1000, 1000);
  await new Promise((res, rej) => { planetImg.onload = res; planetImg.onerror = rej; planetImg.src = planetDataUrl; });
  const planetW = 900, planetH = 900;
  ctx.drawImage(planetImg, (W - planetW)/2, 70, planetW, planetH);

  // Title
  ctx.fillStyle = '#f0e5ff';
  ctx.textAlign = 'center';
  ctx.font = '300 92px "Cormorant Garamond", serif';
  ctx.fillText(planet.name || '—', W/2, 1080);

  // Subtitle
  ctx.fillStyle = 'rgba(240,229,255,0.6)';
  ctx.font = '400 22px "Space Grotesk", sans-serif';
  ctx.fillText(`discovered by ${planet.discoverer || '—'}`, W/2, 1115);

  // Catalogue id + seal
  ctx.fillStyle = 'rgba(240,229,255,0.4)';
  ctx.font = '300 14px "JetBrains Mono", monospace';
  ctx.fillText(`${planet.id} · sealed ${new Date(planet.createdAt||Date.now()).toISOString().slice(0,10)}`, W/2, 1145);

  // Atmosphere bar
  const barX = 140, barY = 1190, barW = W - 280, barH = 24;
  let cursor = barX;
  (planet.atmosphere||[]).forEach(a => {
    const el = window.PLANETALIN_ELEMENTS.find(e => e.sym === a.sym);
    const segW = (a.pct/100) * barW;
    ctx.fillStyle = el?.color || '#888';
    ctx.fillRect(cursor, barY, segW, barH);
    cursor += segW;
  });
  // Bar label
  ctx.fillStyle = 'rgba(240,229,255,0.5)';
  ctx.font = '400 11px "JetBrains Mono", monospace';
  ctx.textAlign = 'left';
  ctx.fillText('ATMOSPHERE', barX, barY - 8);

  // Facts row
  const facts = [
    ['Surface', planet.surface],
    ['Star', (planet.star||'') + '-type'],
    ['Day', (planet.rotationHours||0) + 'h'],
    ['Orbit', (planet.orbitAU||0).toFixed(2) + ' AU'],
    ['Moons', String((planet.moons||[]).length)],
    ['Rings', planet.rings?.enabled ? `${planet.rings.count} bands` : 'none'],
  ];
  const factY = 1280;
  const factCellW = (W - 280) / facts.length;
  facts.forEach(([lbl, val], i) => {
    const fx = barX + i * factCellW + factCellW/2;
    ctx.textAlign = 'center';
    ctx.fillStyle = 'rgba(240,229,255,0.45)';
    ctx.font = '400 11px "JetBrains Mono", monospace';
    ctx.fillText(lbl.toUpperCase(), fx, factY);
    ctx.fillStyle = '#f0e5ff';
    ctx.font = '400 22px "Space Grotesk", sans-serif';
    ctx.fillText(String(val), fx, factY + 30);
  });

  // Seal footer
  if (planet.seal) {
    const sy = H - 90;
    ctx.textAlign = 'center';
    ctx.fillStyle = 'rgba(240,229,255,0.35)';
    ctx.font = '400 11px "JetBrains Mono", monospace';
    ctx.fillText('SEALED BY', W/2, sy);
    ctx.fillStyle = '#f0e5ff';
    ctx.font = 'italic 300 28px "Cormorant Garamond", serif';
    ctx.fillText(`${planet.seal.glyph}  ${planet.seal.name}`, W/2, sy + 34);
  } else {
    ctx.textAlign = 'center';
    ctx.fillStyle = 'rgba(240,229,255,0.3)';
    ctx.font = '400 11px "JetBrains Mono", monospace';
    ctx.fillText('PLANETALIN · CONSTELLATION LAB', W/2, H - 50);
  }

  return canvas.toDataURL('image/png');
}
