/* Custex editor — right-hand preview column. */
const { useState: uS, useRef: uR, useEffect: uE, useMemo: uM } = React;
const { NODE_W, PORT_FIRST, PORT_STRIDE } = window.CxGeom;

const cxLayoutPattern = () => window.CxLayoutPattern || {};
const { nodeTitleFor } = window.CxEditorShared;

const PREV_ZOOM_MIN = 0.35;
const PREV_ZOOM_MAX = 2.5;
const PREVIEW_AREA_STORAGE_KEY = 'custex.previewAreaPx';
const PREVIEW_AREA_MIN = 200;
/** When dragging preview taller, only this much vertical space is reserved for hint + spec; the rest scrolls inside the footer. */
const PREVIEW_FOOTER_MIN_RESERVE = 108;

/** Live-preview vessel: aspect from W×H, scale from area vs 42×42 cm reference (units-aware). */
const previewVesselPxFromCustomSize =
  window.CxMotifPipeline?.previewVesselPxFromCustomSize ||
  function previewVesselPxFromCustomSizeFallback(customSize) {
    const wRaw = Math.max(1, Number(customSize?.w) || 42);
    const hRaw = Math.max(1, Number(customSize?.h) || 42);
    const u = String(customSize?.unit || 'cm').toLowerCase();
    const toCm = (v) => {
      if (u === 'mm') return v / 10;
      if (u === 'inch') return v * 2.54;
      return v;
    };
    const wCm = toCm(wRaw);
    const hCm = toCm(hRaw);
    const aspect = Math.min(4.2, Math.max(0.22, wCm / hCm));
    const refCm2 = 42 * 42;
    const areaCm2 = Math.max(1e-6, wCm * hCm);
    const sizeFactor = Math.sqrt(areaCm2 / refCm2);
    const clamped = Math.min(1.7, Math.max(0.45, sizeFactor));
    const long = Math.min(320, Math.max(96, Math.round(280 * clamped)));
    if (wCm >= hCm) {
      return { width: long, height: Math.round(long / aspect) };
    }
    return { width: Math.round(long * aspect), height: long };
  };

// ─────────── right preview pane ───────────
function CxPreview({ graph, product, craft, material, yarns, motif, totalPrice, priceQuote, validation, theme, previewCollapsed, onPreviewCollapsedChange, onPreviewPaneResizeMouseDown, onPreviewPaneResizeReset }) {
  const CxPB = typeof window !== 'undefined' ? window.CxPriceBreakdown : null;
  const layoutModel = uM(
    () => (cxLayoutPattern().layoutPatternModel || (() => ({})))(graph, 'layout'),
    [
      graph,
      graph.nodes.layout?.data?.tile,
      graph.nodes.layout?.data?.scale,
      graph.nodes.layout?.data?.rotate,
      JSON.stringify(graph.nodes.layout?.data?.motifControls || {}),
      graph.nodes.product?.data?.customSize?.w,
      graph.nodes.product?.data?.customSize?.h,
      graph.nodes.product?.data?.customSize?.unit,
      graph.nodes.motif?.data?.motifId,
      graph.nodes.motif?.data?.generatedImageDataUrl,
      graph.nodes.motif?.data?.generatedSvg,
    ]
  );
  const layout = layoutModel.layout;
  // ground color: material swatch, or blend of yarns, or neutral
  const ground = material?.swatch
    || (yarns && yarns.length ? yarns[0].swatch : '#efeae0');
  const customSize = graph.nodes.product?.data?.customSize;
  const sizeText = customSize
    ? `${customSize.w} × ${customSize.h} ${customSize.unit}`
    : (product?.sizeNote || '—');
  const vesselPx = uM(
    () => previewVesselPxFromCustomSize(customSize),
    [customSize?.w, customSize?.h, customSize?.unit]
  );
  const vesselDimStyle = { width: vesselPx.width, height: vesselPx.height };

  const [previewMode, setPreviewMode] = uS('live');
  const [pz, setPz] = uS(1);
  const viewportRef = uR(null);
  const previewAsideRef = uR(null);
  const [previewAreaHeightPx, setPreviewAreaHeightPx] = uS(() => {
    try {
      const v = parseInt(localStorage.getItem(PREVIEW_AREA_STORAGE_KEY), 10);
      return Number.isFinite(v) && v >= PREVIEW_AREA_MIN ? v : null;
    } catch {
      return null;
    }
  });

  const clampPreviewAreaHeight = (h) => {
    if (!Number.isFinite(h)) return PREVIEW_AREA_MIN;
    const aside = previewAsideRef.current;
    if (!aside) return Math.max(PREVIEW_AREA_MIN, h);
    const bar = aside.querySelector('.cx-prev-bar');
    const handleEl = aside.querySelector('.cx-prev-resize-handle');
    const footerMin = 120;
    const fixed =
      (bar?.offsetHeight ?? 0) +
      (handleEl?.offsetHeight ?? 12) +
      footerMin +
      2;
    const maxH = Math.max(PREVIEW_AREA_MIN, aside.clientHeight - fixed);
    return Math.min(maxH, Math.max(PREVIEW_AREA_MIN, h));
  };

  uE(() => {
    if (previewCollapsed) return undefined;
    const fix = () => {
      setPreviewAreaHeightPx((prev) => {
        if (prev == null) return null;
        const next = clampPreviewAreaHeight(prev);
        return next === prev ? prev : next;
      });
    };
    const id = requestAnimationFrame(fix);
    window.addEventListener('resize', fix);
    return () => {
      cancelAnimationFrame(id);
      window.removeEventListener('resize', fix);
    };
  }, [previewCollapsed]);

  const onPreviewResizeMouseDown = (e) => {
    if (e.button !== 0) return;
    e.preventDefault();
    e.stopPropagation();
    const area = viewportRef.current;
    if (!area) return;
    const startY = e.clientY;
    const startH = previewAreaHeightPx ?? area.getBoundingClientRect().height;
    setPreviewAreaHeightPx(clampPreviewAreaHeight(startH));
    const move = (ev) => {
      setPreviewAreaHeightPx(clampPreviewAreaHeight(startH + (ev.clientY - startY)));
    };
    const up = () => {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
      try {
        const h = viewportRef.current?.getBoundingClientRect().height;
        if (h && h >= PREVIEW_AREA_MIN) {
          localStorage.setItem(PREVIEW_AREA_STORAGE_KEY, String(Math.round(h)));
        }
      } catch {
        /* ignore */
      }
    };
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
  };

  const onPreviewResizeReset = () => {
    setPreviewAreaHeightPx(null);
    try {
      localStorage.removeItem(PREVIEW_AREA_STORAGE_KEY);
    } catch {
      /* ignore */
    }
  };

  uE(() => {
    const el = viewportRef.current;
    if (!el || previewCollapsed) return undefined;
    const onWheel = (e) => {
      e.preventDefault();
      e.stopPropagation();
      const dir = e.deltaY > 0 ? 0.92 : 1.08;
      setPz((z) => Math.min(PREV_ZOOM_MAX, Math.max(PREV_ZOOM_MIN, z * dir)));
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, [previewCollapsed]);

  const [toolbarMsg, setToolbarMsg] = uS(null);
  const toolbarMsgTimerRef = uR(null);
  const flashMsg = (text, ms = 2200) => {
    setToolbarMsg(text);
    if (toolbarMsgTimerRef.current) window.clearTimeout(toolbarMsgTimerRef.current);
    toolbarMsgTimerRef.current = window.setTimeout(() => setToolbarMsg(null), ms);
  };

  const onPrevSave = () => {
    try {
      localStorage.setItem(
        'custex.livePreview.saved',
        JSON.stringify({
          at: new Date().toISOString(),
          productId: product?.id,
          productName: product?.name,
          craftId: craft?.id,
          motifId: motif?.id,
          qty: graph.nodes.output?.data?.qty,
        })
      );
      flashMsg('Saved in this browser');
    } catch {
      flashMsg('Could not save');
    }
  };

  const onPrevDownload = () => {
    const first = layoutModel.tileMotifs[0];
    const motifInner = first?.motif?.svg || '<circle cx="40" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="2"/>';
    const isRasterTile = /^<\s*image\b/i.test(String(motifInner).trim());
    const safeGround = (ground || '#efeae0').replace(/[<"]/g, '');
    const vectorFrame = `<g transform="translate(160,160)" fill="none" stroke="rgba(58,47,38,0.72)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
    <g transform="scale(1.85)">${motifInner}</g>
  </g>`;
    const rasterFrame = `<g transform="translate(160,160) scale(1.85)">${motifInner}</g>`;
    const svg = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320" viewBox="0 0 320 320">
  <title>${(product?.name || 'Custex').replace(/</g, '')}</title>
  <rect width="320" height="320" rx="24" fill="${safeGround}"/>
  ${isRasterTile ? rasterFrame : vectorFrame}
</svg>`;
    const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `custex-preview-${product?.id || 'export'}.svg`;
    a.click();
    URL.revokeObjectURL(url);
    flashMsg('SVG downloaded');
  };

  const onPrevShare = async () => {
    const title = 'Custex — live preview';
    const text = [product?.name, craft?.label, motif?.name].filter(Boolean).join(' · ') || 'Textile workflow';
    const url = window.location.href;
    try {
      if (navigator.share) {
        await navigator.share({ title, text, url });
      } else if (navigator.clipboard?.writeText) {
        await navigator.clipboard.writeText(url);
        flashMsg('Link copied');
      } else {
        window.prompt('Copy link:', url);
      }
    } catch (e) {
      if (e.name === 'AbortError') return;
      flashMsg('Could not share');
    }
  };

  const patternInner = (
    <>
      {craft?.inputKind === 'yarn' && yarns.length > 0 && (
        <div className="cx-prev-weave"
          style={{
            position: 'absolute', inset: 0,
            background: yarns.length > 1
              ? `repeating-linear-gradient(0deg, ${yarns[0].swatch} 0 4px, ${yarns[1].swatch} 4px 8px), repeating-linear-gradient(90deg, ${yarns[0].swatch} 0 4px, transparent 4px 8px)`
              : `repeating-linear-gradient(45deg, ${yarns[0].swatch} 0 3px, rgba(58,47,38,0.08) 3px 6px)`,
            opacity: 0.7,
          }} />
      )}
      {React.createElement(cxLayoutPattern().CxLayoutPatternGrid || (() => null), { model: layoutModel })}
    </>
  );

  const arNode = graph.nodes.airender;
  const arData = arNode?.data || {};
  const renderPrompt = (arData.renderPrompt || '').trim();
  const refUrl = arData.refImageDataUrl || null;
  const renderedUrl = arData.renderImageDataUrl || arData.renderImageUrl || null;
  const matLine = material?.name || (yarns[0]?.name + (yarns.length > 1 ? ` +${yarns.length - 1}` : '')) || '—';
  const motifLabel = motif?.name || graph.nodes.motif?.data?.generatedName || '—';

  const liveBody = (
    <div className="cx-prev-mock">
      <div className="cx-prev-vessel" style={{ background: ground, ...vesselDimStyle }}>
        {patternInner}
      </div>
      <div className="cx-prev-caption">
        <span className="cx-prev-cap-k">live</span>
        <span className="cx-prev-cap-v">{product?.name} · {matLine}</span>
      </div>
    </div>
  );

  const renderBody = !arNode || arNode.hidden ? (
    <div className="cx-prev-mock">
      <div className="cx-prev-vessel cx-prev-vessel-empty" style={vesselDimStyle}>
        <p className="cx-prev-empty-msg">Show the <strong>AI Render</strong> node on the canvas to preview scene, reference photo, and photoreal output notes.</p>
      </div>
      <div className="cx-prev-caption">
        <span className="cx-prev-cap-k">render</span>
        <span className="cx-prev-cap-v">—</span>
      </div>
    </div>
  ) : (
    <div className="cx-prev-mock cx-prev-mock-rendermode">
      <div
        className={`cx-prev-vessel cx-prev-vessel-render${(renderedUrl || refUrl) ? ' cx-prev-vessel-render-ref' : ''}`}
        style={(renderedUrl || refUrl) ? { ...vesselDimStyle } : { background: ground, ...vesselDimStyle }}
      >
        {renderedUrl ? (
          <img src={renderedUrl} alt="" className="cx-prev-render-photo" />
        ) : refUrl ? (
          <img src={refUrl} alt="" className="cx-prev-render-photo" />
        ) : (
          <div className="cx-prev-render-fallback">
            <div className="cx-prev-render-fallback-inner">{patternInner}</div>
          </div>
        )}
        <div className="cx-prev-render-scrim">
          <span className="cx-prev-render-eyebrow">AI render</span>
          <p className="cx-prev-render-promptline">{renderPrompt || 'Add scene & lighting in the AI Render node — optional reference photo.'}</p>
          {renderedUrl ? <span className="cx-prev-render-refname">Generated by API</span> : null}
          {arData.refImageName ? <span className="cx-prev-render-refname">Ref: {arData.refImageName}</span> : null}
        </div>
      </div>
      <div className="cx-prev-caption">
        <span className="cx-prev-cap-k">render</span>
        <span className="cx-prev-cap-v">{renderedUrl ? 'AI output' : (refUrl ? 'Reference + brief' : 'Layout + brief')}</span>
      </div>
    </div>
  );

  if (previewCollapsed) {
    return (
      <aside className="cx-preview cx-preview-collapsed" aria-label="Preview collapsed">
        <button type="button" className="cx-prev-collapse-expand" onClick={() => onPreviewCollapsedChange && onPreviewCollapsedChange(false)} title="Expand preview" aria-label="Expand preview panel">
          <svg className="cx-prev-ico-svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="M6 3 11 8 6 13" />
          </svg>
        </button>
        <span className="cx-preview-collapsed-label">Preview</span>
      </aside>
    );
  }

  return (
    <aside className="cx-preview" ref={previewAsideRef} aria-label="Live preview">
      {typeof onPreviewPaneResizeMouseDown === 'function' ? (
        <div
          className="cx-preview-resize-w"
          role="separator"
          aria-orientation="vertical"
          aria-label="Drag to resize preview column width"
          title="Drag left to widen, right to narrow · Double-click to reset width"
          onMouseDown={onPreviewPaneResizeMouseDown}
          onDoubleClick={onPreviewPaneResizeReset}
        />
      ) : null}
      <div className="cx-prev-bar">
        <div className="cx-prev-bar-row cx-prev-bar-row-head">
          <span className="cx-prev-l">{previewMode === 'live' ? 'Pattern preview' : 'AI render preview'}</span>
          <div className="cx-prev-actions-icons">
            <button type="button" className="cx-prev-ico cx-prev-collapse" onClick={() => onPreviewCollapsedChange && onPreviewCollapsedChange(true)} title="Collapse preview" aria-label="Collapse preview panel">
              <svg className="cx-prev-ico-svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <path d="M10 3 5 8l5 5" />
              </svg>
            </button>
            {toolbarMsg ? <span className="cx-prev-toolbar-msg">{toolbarMsg}</span> : null}
            <button type="button" className="cx-prev-ico primary" onClick={onPrevSave} title="Save" aria-label="Save preview snapshot">
              <svg className="cx-prev-ico-svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <path d="M4 14h9a1 1 0 0 0 1-1V5.5L9.5 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1z" />
                <path d="M9 2v4h3.5" />
                <path d="M5 10h6" />
              </svg>
            </button>
            <button type="button" className="cx-prev-ico" onClick={onPrevDownload} title="Download" aria-label="Download preview as SVG">
              <svg className="cx-prev-ico-svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <path d="M8 3v7.5" />
                <path d="m5.5 8 2.5 2.5L10.5 8" />
                <path d="M3.5 13.5h9" />
              </svg>
            </button>
            <button type="button" className="cx-prev-ico" onClick={onPrevShare} title="Share" aria-label="Share or copy link">
              <svg className="cx-prev-ico-svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <circle cx="11.5" cy="4.5" r="1.75" />
                <circle cx="4.5" cy="8" r="1.75" />
                <circle cx="11.5" cy="11.5" r="1.75" />
                <path d="M6.1 6.9 9.9 5.1M6.1 9.1l3.8 1.8" />
              </svg>
            </button>
          </div>
        </div>
        <div className="cx-prev-bar-row cx-prev-bar-row-tools">
          <div className="cx-prev-mode" role="tablist" aria-label="Preview mode">
            <button type="button" role="tab" aria-selected={previewMode === 'live'} className={`cx-prev-mode-btn${previewMode === 'live' ? ' on' : ''}`} onClick={() => setPreviewMode('live')}>Live</button>
            <button type="button" role="tab" aria-selected={previewMode === 'render'} className={`cx-prev-mode-btn${previewMode === 'render' ? ' on' : ''}`} onClick={() => setPreviewMode('render')}>Render</button>
          </div>
          <div className="cx-prev-zoombar" aria-label="Preview zoom">
            <button type="button" className="cx-prev-zoom-mini" onClick={() => setPz((z) => Math.min(PREV_ZOOM_MAX, z * 1.15))} title="Zoom in" aria-label="Zoom in">+</button>
            <span className="cx-prev-zoom-pct">{Math.round(pz * 100)}%</span>
            <button type="button" className="cx-prev-zoom-mini" onClick={() => setPz((z) => Math.max(PREV_ZOOM_MIN, z / 1.15))} title="Zoom out" aria-label="Zoom out">−</button>
            <button type="button" className="cx-prev-zoom-mini cx-prev-zoom-reset" onClick={() => setPz(1)} title="Reset zoom" aria-label="Reset zoom">⊙</button>
          </div>
        </div>
        <span className="cx-prev-r">{product?.name} · {craft?.short}</span>
      </div>
      <div className="cx-prev-body">
        <div
          className={`cx-prev-area${previewAreaHeightPx != null ? ' cx-prev-area-sized' : ''}`}
          ref={viewportRef}
          style={previewAreaHeightPx != null ? { height: Math.round(previewAreaHeightPx) } : undefined}
        >
          <div className="cx-prev-scale-wrap" style={{ transform: `scale(${pz})` }}>
            {previewMode === 'live' ? liveBody : renderBody}
          </div>
        </div>
        <div
          className="cx-prev-resize-handle"
          role="separator"
          aria-orientation="horizontal"
          aria-label="Drag to resize preview area height"
          title="Drag down to enlarge preview; details below shrink and scroll · Double-click to reset height"
          onMouseDown={onPreviewResizeMouseDown}
          onDoubleClick={onPreviewResizeReset}
        />
        <div
          className={`cx-prev-footer${previewAreaHeightPx != null ? ' cx-prev-footer-scroll' : ''}`}
        >
          <p className="cx-prev-hint">Scroll in the preview to zoom · {previewMode === 'live' ? 'Live' : 'Render'} view</p>
          <div className="cx-prev-tp">
            <div className="cx-tp-row"><span className="k">Product</span><span className="v">{product?.name || '—'}</span></div>
            <div className="cx-tp-row"><span className="k">Size</span><span className="v">{sizeText}</span></div>
            <div className="cx-tp-row"><span className="k">Craft</span><span className="v">{craft?.label || '—'}</span></div>
            {material && <div className="cx-tp-row"><span className="k">Material</span><span className="v">{material.name} · {material.gsm}gsm</span></div>}
            {yarns.length > 0 && <div className="cx-tp-row"><span className="k">Yarn</span><span className="v">{yarns.map(y=>y.name).join(' + ')}</span></div>}
            <div className="cx-tp-row"><span className="k">Motif</span><span className="v">{motifLabel}</span></div>
            <div className="cx-tp-row"><span className="k">Quantity</span><span className="v">×{graph.nodes.output?.data?.qty ?? '—'}</span></div>
            {priceQuote && CxPB ? <CxPB quote={priceQuote} validation={validation} /> : null}
            <div className="cx-tp-row total"><span className="k">Estimated total</span><span className="v">£{totalPrice.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span></div>
            <p className="cx-price-shipping-note">Excludes shipping &amp; delivery</p>
          </div>
        </div>
      </div>
    </aside>
  );
}

window.CxPreviewPanel = { CxPreview };
