// Admin · Artwork studio
// Client-side artwork prep for embroidery: background removal, enlarge & smooth,
// and vectorize to SVG. Everything runs in the browser — no uploads, no API keys.
// The vectorizer (ImageTracer) is loaded on demand from the same unpkg CDN the
// rest of the app uses. Matches the app's tokens / .stitched / .btn / .field styles.

// Lazy-load ImageTracer (global `ImageTracer`) only when the artist first traces.
function loadImageTracer() {
  if (window.ImageTracer) return Promise.resolve(window.ImageTracer);
  if (window.__itracerPromise) return window.__itracerPromise;
  window.__itracerPromise = new Promise((resolve, reject) => {
    const s = document.createElement("script");
    s.src = "https://unpkg.com/imagetracerjs@1.2.6/imagetracer_v1.2.6.js";
    s.async = true;
    s.onload = () => resolve(window.ImageTracer);
    s.onerror = () => reject(new Error("Couldn't load the vectorizer. Check the connection and try again."));
    document.head.appendChild(s);
  });
  return window.__itracerPromise;
}

// Sample the four corners of an ImageData to guess the background colour.
function detectKeyColor(data, w, h) {
  const pts = [
    [0, 0], [w - 1, 0], [0, h - 1], [w - 1, h - 1],
    [Math.floor(w / 2), 0], [Math.floor(w / 2), h - 1],
  ];
  let r = 0, g = 0, b = 0;
  for (const [x, y] of pts) {
    const i = (y * w + x) * 4;
    r += data[i]; g += data[i + 1]; b += data[i + 2];
  }
  const n = pts.length;
  return { r: Math.round(r / n), g: Math.round(g / n), b: Math.round(b / n) };
}

// Knock out pixels close to `key`, with a soft feathered edge. Mutates in place.
function knockOutBackground(imgData, key, tolerancePct, featherPct) {
  const d = imgData.data;
  const maxDist = 442;                          // sqrt(255^2 * 3), the diagonal of RGB space
  const tol = (tolerancePct / 100) * maxDist;
  const band = (featherPct / 100) * maxDist;    // width of the soft edge beyond `tol`
  for (let i = 0; i < d.length; i += 4) {
    const dr = d[i] - key.r, dg = d[i + 1] - key.g, db = d[i + 2] - key.b;
    const dist = Math.sqrt(dr * dr + dg * dg + db * db);
    if (dist <= tol) {
      d[i + 3] = 0;
    } else if (band > 0 && dist <= tol + band) {
      const t = (dist - tol) / band;             // 0 at edge of cut, 1 at edge of band
      d[i + 3] = Math.round(d[i + 3] * t);
    }
  }
}

function ArtworkStudio() {
  const app = useApp();

  const [fileName, setFileName] = useState("");
  const [origUrl, setOrigUrl]   = useState("");
  const [dims, setDims]         = useState(null);     // { w, h } of the source
  const imgRef = useRef(null);                        // decoded source <img>
  const liveRef = useRef(null);                       // live preview canvas
  const liveTimer = useRef(null);                     // debounce handle for live render

  const [removeBg, setRemoveBg] = useState(true);
  const [tolerance, setTolerance] = useState(18);     // % of colour-space distance
  const [feather, setFeather]     = useState(6);      // % soft edge
  const [scale, setScale]         = useState(1);      // 1–4× enlarge

  const [resultUrl, setResultUrl] = useState("");     // processed PNG (data URL)
  const [showOriginal, setShowOriginal] = useState(false);

  const [traceColors, setTraceColors] = useState(8);
  const [detail, setDetail]           = useState("balanced"); // crisp | balanced | smooth
  const [svg, setSvg]                 = useState("");

  const [busy, setBusy]   = useState("");             // "" | label of current task
  const [error, setError] = useState("");
  const [note, setNote]   = useState("");

  const reset = () => {
    setResultUrl(""); setSvg(""); setError(""); setNote(""); setShowOriginal(false);
  };

  const onPick = async (file) => {
    if (!file) return;
    if (!file.type || !file.type.startsWith("image/") || file.type === "image/svg+xml") {
      setError("Please choose a PNG, JPG or WebP image (not an SVG)."); return;
    }
    reset();
    setFileName(file.name || "artwork");
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      imgRef.current = img;
      setDims({ w: img.naturalWidth, h: img.naturalHeight });
      setOrigUrl(url);
    };
    img.onerror = () => { setError("That image wouldn't open. Try a different file."); URL.revokeObjectURL(url); };
    img.src = url;
  };

  // Background removal + enlarge → a processed PNG data URL.
  const process = async () => {
    const img = imgRef.current;
    if (!img) return;
    setBusy("Working the image"); setError(""); setNote("");
    try {
      // Cap the working size so very large photos stay responsive; enlarge applies after.
      const cap = 2200;
      const base = Math.min(1, cap / Math.max(img.naturalWidth, img.naturalHeight));
      const bw = Math.max(1, Math.round(img.naturalWidth * base));
      const bh = Math.max(1, Math.round(img.naturalHeight * base));

      const work = document.createElement("canvas");
      work.width = bw; work.height = bh;
      const wctx = work.getContext("2d");
      wctx.drawImage(img, 0, 0, bw, bh);

      if (removeBg) {
        const id = wctx.getImageData(0, 0, bw, bh);
        const key = detectKeyColor(id.data, bw, bh);
        knockOutBackground(id, key, tolerance, feather);
        wctx.putImageData(id, 0, 0);
      }

      // Enlarge & smooth (honest interpolation — not invented detail).
      const out = document.createElement("canvas");
      out.width = Math.round(bw * scale);
      out.height = Math.round(bh * scale);
      const octx = out.getContext("2d");
      octx.imageSmoothingEnabled = true;
      octx.imageSmoothingQuality = "high";
      octx.clearRect(0, 0, out.width, out.height);
      octx.drawImage(work, 0, 0, out.width, out.height);

      setResultUrl(out.toDataURL("image/png"));
      window.__studioCanvas = out; // kept for trace / save without re-decoding
    } catch (e) {
      setError("Something went wrong processing that image.");
    } finally {
      setBusy("");
    }
  };

  const vectorize = async () => {
    const canvas = window.__studioCanvas;
    if (!canvas) { setError("Process the image first, then vectorize."); return; }
    setBusy("Tracing to SVG"); setError(""); setNote("");
    try {
      const ImageTracer = await loadImageTracer();
      const ctx = canvas.getContext("2d");
      const id = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const presets = {
        crisp:    { ltres: 0.1, qtres: 0.1, pathomit: 1 },
        balanced: { ltres: 1,   qtres: 1,   pathomit: 8 },
        smooth:   { ltres: 4,   qtres: 4,   pathomit: 16 },
      };
      const opts = Object.assign(
        { numberofcolors: traceColors, colorquantcycles: 3, scale: 1, roundcoords: 2 },
        presets[detail] || presets.balanced
      );
      const out = ImageTracer.imagedataToSVG(id, opts);
      setSvg(out);
    } catch (e) {
      setError(e.message || "Couldn't trace that image.");
    } finally {
      setBusy("");
    }
  };

  const download = (href, ext) => {
    const a = document.createElement("a");
    a.href = href;
    a.download = (fileName.replace(/\.[^.]+$/, "") || "artwork") + "-studio." + ext;
    document.body.appendChild(a); a.click(); a.remove();
  };
  const downloadPng = () => resultUrl && download(resultUrl, "png");
  const downloadSvg = () => {
    if (!svg) return;
    const blob = new Blob([svg], { type: "image/svg+xml" });
    const url = URL.createObjectURL(blob);
    download(url, "svg");
    setTimeout(() => URL.revokeObjectURL(url), 4000);
  };
  const copySvg = async () => {
    if (!svg) return;
    try { await navigator.clipboard.writeText(svg); setNote("SVG copied to clipboard."); }
    catch { setError("Couldn't copy — try the download instead."); }
  };

  const saveToLibrary = async () => {
    const canvas = window.__studioCanvas;
    if (!canvas || app.mode !== "api" || !app.addLogo) return;
    setBusy("Saving to library"); setError(""); setNote("");
    try {
      const blob = await new Promise((res) => canvas.toBlob(res, "image/png"));
      const name = (fileName.replace(/\.[^.]+$/, "") || "artwork") + " (prepped)";
      const file = new File([blob], name + ".png", { type: "image/png" });
      const thumb = canvas.toDataURL("image/png");
      await app.addLogo(file, name, thumb);
      setNote("Saved to your logo library.");
    } catch (e) {
      setError("Couldn't save to the library.");
    } finally {
      setBusy("");
    }
  };

  // Live preview — re-renders the background knock-out at a small working size
  // as the sliders move, so what you see matches the current settings before
  // Apply commits the full-resolution version.
  const renderLive = () => {
    const canvas = liveRef.current, img = imgRef.current;
    if (!canvas || !img) return;
    const cap = 760;
    const base = Math.min(1, cap / Math.max(img.naturalWidth, img.naturalHeight));
    const bw = Math.max(1, Math.round(img.naturalWidth * base));
    const bh = Math.max(1, Math.round(img.naturalHeight * base));
    canvas.width = bw; canvas.height = bh;
    const ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, bw, bh);
    ctx.drawImage(img, 0, 0, bw, bh);
    if (removeBg) {
      const id = ctx.getImageData(0, 0, bw, bh);
      const key = detectKeyColor(id.data, bw, bh);
      knockOutBackground(id, key, tolerance, feather);
      ctx.putImageData(id, 0, 0);
    }
  };
  useEffect(() => {
    if (!dims || showOriginal || svg) return;       // canvas only visible in these cases
    clearTimeout(liveTimer.current);
    liveTimer.current = setTimeout(renderLive, 70);  // debounce slider drags
    return () => clearTimeout(liveTimer.current);
  }, [dims, removeBg, tolerance, feather, showOriginal, svg]);

  const swatch = (label, val, set, min, max, step, suffix) => (
    <div className="field" style={{ marginBottom: 12 }}>
      <label style={{ display: "flex", justifyContent: "space-between" }}>
        <span>{label}</span>
        <span style={{ color: "var(--ink-3)" }}>{val}{suffix || ""}</span>
      </label>
      <input type="range" min={min} max={max} step={step || 1} value={val}
        onChange={(e) => set(Number(e.target.value))} style={{ width: "100%" }} />
    </div>
  );

  return (
    <div>
      <div style={{ marginBottom: 18 }}>
        <h2 style={{ margin: 0 }}>Artwork studio</h2>
        <div style={{ color: "var(--ink-3)", fontSize: 14, marginTop: 4 }}>
          Lift a logo off its background, enlarge it, and trace it to a clean SVG — all in your browser.
          Nothing leaves this page until you save or download it.
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "minmax(260px, 340px) 1fr", gap: 20, alignItems: "start" }}>

        {/* ---- Controls ---- */}
        <div className="stitched stitched-sage" style={{ padding: 18 }}>
          <div className="field" style={{ marginBottom: 16 }}>
            <label>1 · Choose artwork</label>
            <input type="file" accept="image/png,image/jpeg,image/webp"
              onChange={(e) => onPick(e.target.files && e.target.files[0])} />
            {dims && <div style={{ color: "var(--ink-3)", fontSize: 13, marginTop: 6 }}>
              {dims.w} × {dims.h}px source
            </div>}
          </div>

          <div className="field" style={{ marginBottom: 12 }}>
            <label style={{ display: "flex", gap: 8, alignItems: "center", cursor: "pointer" }}>
              <input type="checkbox" checked={removeBg} onChange={(e) => setRemoveBg(e.target.checked)} />
              <span>2 · Remove background</span>
            </label>
            <div style={{ color: "var(--ink-3)", fontSize: 12, marginTop: 4 }}>
              Works best on a solid or near-solid background (a logo on white).
            </div>
          </div>

          {removeBg && (
            <div style={{ paddingLeft: 4, borderLeft: "2px solid var(--line)", marginLeft: 2 }}>
              {swatch("Tolerance", tolerance, setTolerance, 2, 60, 1, "%")}
              {swatch("Soft edge", feather, setFeather, 0, 25, 1, "%")}
            </div>
          )}

          <div className="field" style={{ margin: "12px 0" }}>
            <label>3 · Enlarge</label>
            <select value={scale} onChange={(e) => setScale(Number(e.target.value))}>
              <option value={1}>Original size</option>
              <option value={2}>2× larger</option>
              <option value={3}>3× larger</option>
              <option value={4}>4× larger</option>
            </select>
            <div style={{ color: "var(--ink-3)", fontSize: 12, marginTop: 4 }}>
              Smooth enlargement. For true infinite scale, trace to SVG below.
            </div>
          </div>

          <button className="btn" style={{ width: "100%" }} disabled={!dims || !!busy} onClick={process}>
            {busy === "Working the image" ? "Working…" : "Apply"}
          </button>

          <hr style={{ border: 0, borderTop: "1px solid var(--line)", margin: "18px 0" }} />

          <div className="field" style={{ marginBottom: 12 }}>
            <label>4 · Vectorize to SVG</label>
            <div style={{ color: "var(--ink-3)", fontSize: 12, marginTop: 4 }}>
              Turns the prepped image into scalable vector paths — ideal for digitizing.
            </div>
          </div>
          {swatch("Colours", traceColors, setTraceColors, 2, 24, 1, "")}
          <div className="field" style={{ marginBottom: 12 }}>
            <label>Detail</label>
            <select value={detail} onChange={(e) => setDetail(e.target.value)}>
              <option value="crisp">Crisp (more paths)</option>
              <option value="balanced">Balanced</option>
              <option value="smooth">Smooth (fewer paths)</option>
            </select>
          </div>
          <button className="btn btn-ghost" style={{ width: "100%" }} disabled={!resultUrl || !!busy} onClick={vectorize}>
            {busy === "Tracing to SVG" ? "Tracing…" : "Trace to SVG"}
          </button>
        </div>

        {/* ---- Preview ---- */}
        <div className="stitched" style={{ padding: 18, minHeight: 320 }}>
          {error && <div style={{ color: "var(--rose-deep)", fontSize: 14, marginBottom: 12 }}>{error}</div>}
          {note &&  <div style={{ color: "var(--sage-deep)", fontSize: 14, marginBottom: 12 }}>{note}</div>}

          {!dims && (
            <div style={{ textAlign: "center", color: "var(--ink-3)", padding: "60px 20px" }}>
              Choose an image to begin.
            </div>
          )}

          {dims && (
            <div>
              <div style={{ display: "flex", gap: 10, marginBottom: 12, flexWrap: "wrap", alignItems: "center" }}>
                {dims && (
                  <button className="btn btn-ghost btn-small" onClick={() => setShowOriginal(s => !s)}>
                    {showOriginal ? "Show result" : "Show original"}
                  </button>
                )}
                <div style={{ flex: 1 }} />
                {resultUrl && <button className="btn btn-ghost btn-small" onClick={downloadPng}>Download PNG</button>}
                {svg && <button className="btn btn-ghost btn-small" onClick={downloadSvg}>Download SVG</button>}
                {svg && <button className="btn btn-ghost btn-small" onClick={copySvg}>Copy SVG</button>}
                {resultUrl && app.mode === "api" && app.addLogo &&
                  <button className="btn btn-ghost btn-small" onClick={saveToLibrary}>Save to library</button>}
              </div>

              {!svg && !showOriginal && (
                <div style={{ color: "var(--ink-3)", fontSize: 12, marginBottom: 8 }}>
                  Live preview — adjust the sliders and watch it update, then press Apply to commit (and unlock enlarge, download &amp; trace).
                </div>
              )}

              {/* Checkerboard backing so transparency is visible */}
              <div style={{
                borderRadius: 8, padding: 12, display: "flex", justifyContent: "center", alignItems: "center",
                minHeight: 240,
                backgroundImage: "linear-gradient(45deg,#e9e9e9 25%,transparent 25%),linear-gradient(-45deg,#e9e9e9 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#e9e9e9 75%),linear-gradient(-45deg,transparent 75%,#e9e9e9 75%)",
                backgroundSize: "20px 20px",
                backgroundPosition: "0 0,0 10px,10px -10px,-10px 0",
              }}>
                {showOriginal
                  ? <img src={origUrl} alt="original"
                         style={{ maxWidth: "100%", maxHeight: 460, objectFit: "contain" }} />
                  : svg
                    ? <div style={{ maxWidth: "100%", maxHeight: 460, overflow: "auto" }}
                           dangerouslySetInnerHTML={{ __html: svg }} />
                    : <canvas ref={liveRef} aria-label="live preview"
                         style={{ maxWidth: "100%", maxHeight: 460, width: "auto", height: "auto", display: "block" }} />}
              </div>

              {svg && <div style={{ color: "var(--ink-3)", fontSize: 12, marginTop: 8 }}>
                SVG ready — {Math.round(svg.length / 1024)}KB. Download or copy it above.
              </div>}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ArtworkStudio });
