// Diagram Manager — upload charts/diagrams to specific sections
const { useState: useStateD, useEffect: useEffectD, useMemo: useMemoD, useRef: useRefD } = React;

const API_BASE_D = window.API_BASE || "https://generation-console.integermarket.com";

function DiagramManager({ reports, preselectVersionId, preselectTitle, onBack, pushToast }) {
  // Reports available — we treat each report as a "version"
  const availableVersions = useMemoD(() => {
    const list = reports.map((r) => ({ id: r.id, title: r.title, status: r.status }));
    // If preselect is not in list, prepend it
    if (preselectVersionId && !list.find((v) => v.id === preselectVersionId)) {
      list.unshift({ id: preselectVersionId, title: preselectTitle || "Current report", status: "generating" });
    }
    return list;
  }, [reports, preselectVersionId, preselectTitle]);

  const [versionId, setVersionId] = useStateD(preselectVersionId || availableVersions[0]?.id);
  const currentVersion = availableVersions.find((v) => v.id === versionId);

  // Real sections fetched from API when version changes
  const [sections, setSections] = useStateD([]);
  const tree = useMemoD(() => buildTreeD(sections), [sections]);
  const [collapsed, setCollapsed] = useStateD({});
  const [selectedSec, setSelectedSec] = useStateD(null);
  const selectedSection = sections.find((s) => s.num === selectedSec);

  useEffectD(() => {
    if (!versionId) return;
    fetch(`${API_BASE_D}/reports/version/${versionId}/sections`)
      .then((r) => r.ok ? r.json() : null)
      .then((data) => {
        if (!data?.sections?.length) return;
        const flatten = (nodes, parentNum = "") => {
          const out = [];
          nodes.forEach((n, i) => {
            const num = parentNum ? `${parentNum}.${n.order ?? i + 1}` : String(n.order ?? i + 1);
            out.push({ num, title: n.title, id: n.id });
            if (n.children?.length) out.push(...flatten(n.children, num));
          });
          return out;
        };
        const flat = flatten(data.sections);
        setSections(flat);
        setSelectedSec(flat[2]?.num || flat[0]?.num);
      })
      .catch(() => setSections([]));
  }, [versionId]);

  // image library: { [versionId]: { [sectionNum]: [{id, url, name, date}] } }
  const [library, setLibrary] = useStateD({});

  // Load ALL images for this version in one call whenever sections are loaded
  useEffectD(() => {
    if (!versionId || !sections.length) return;
    const idToNum = {};
    sections.forEach((s) => { if (s.id) idToNum[s.id] = s.num; });
    fetch(`${API_BASE_D}/reports/versions/${versionId}/images`)
      .then((r) => r.ok ? r.json() : null)
      .then((data) => {
        if (!data?.images_by_section) return;
        const byNum = {};
        Object.entries(data.images_by_section).forEach(([sid, imgs]) => {
          const num = idToNum[sid];
          if (!num) return;
          byNum[num] = imgs.map((img) => ({
            id: img.id,
            url: img.file_url,
            name: img.name,
            date: img.generated_at ? new Date(img.generated_at).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "",
          }));
        });
        setLibrary((lib) => ({ ...lib, [versionId]: { ...(lib[versionId] || {}), ...byNum } }));
      })
      .catch(() => {});
  }, [versionId, sections]);

  // cover images per version: { [versionId]: { url, date, name } | null }
  const [covers, setCovers] = useStateD({});
  const [coverUploading, setCoverUploading] = useStateD(false);
  const coverInputRef = useRefD(null);
  const currentCover = covers[versionId] || null;

  // Load cover image from backend when version changes
  useEffectD(() => {
    if (!versionId) return;
    fetch(`${API_BASE_D}/reports/versions/${versionId}/cover-image`)
      .then((r) => r.ok ? r.json() : null)
      .then((data) => {
        if (data?.cover_image_url) {
          setCovers((c) => ({ ...c, [versionId]: { url: data.cover_image_url, date: data.uploaded_at ? new Date(data.uploaded_at).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "", name: "cover" } }));
        }
      })
      .catch(() => {});
  }, [versionId]);

  const handleCoverFiles = async (files) => {
    if (!files || files.length === 0) return;
    const file = files[0];
    if (!file.type.startsWith("image/")) { pushToast("Upload failed — file must be an image", "error"); return; }
    setCoverUploading(true);
    try {
      const fd = new FormData();
      fd.append("image", file);
      const r = await fetch(`${API_BASE_D}/reports/versions/${versionId}/upload-cover-image`, { method: "POST", body: fd });
      if (!r.ok) throw new Error(await r.text());
      const data = await r.json();
      setCovers((c) => ({ ...c, [versionId]: { url: data.cover_image_url, date: formatDate(new Date()), name: file.name } }));
      pushToast("Cover image updated", "success");
    } catch (e) {
      pushToast(`Upload failed — ${e.message}`, "error");
    } finally {
      setCoverUploading(false);
    }
  };

  const sectionImages = library[versionId]?.[selectedSec] || [];

  // Upload state
  const [dragging, setDragging] = useStateD(false);
  const [label, setLabel] = useStateD("");
  const [uploading, setUploading] = useStateD(false);
  const [progress, setProgress] = useStateD(0);
  const [pendingDelete, setPendingDelete] = useStateD(null);
  const [pdfUrl, setPdfUrl] = useStateD(null);
  const [pdfGenerating, setPdfGenerating] = useStateD(false);
  const fileInputRef = useRefD(null);

  const handleFiles = async (files) => {
    if (!files || files.length === 0) return;
    const sectionId = selectedSection?.id;
    if (!sectionId) { pushToast("Select a section first", "error"); return; }
    setUploading(true);
    setProgress(0);
    const total = files.length;
    for (let i = 0; i < total; i++) {
      const file = files[i];
      try {
        setProgress(((i + 0.1) / total) * 100);
        const fd = new FormData();
        fd.append("image", file);
        if (label.trim()) fd.append("name", label.trim());
        const r = await fetch(`${API_BASE_D}/reports/versions/${versionId}/sections/${sectionId}/upload-image`, { method: "POST", body: fd });
        if (!r.ok) throw new Error(await r.text());
        const data = await r.json();
        const preview = await readAsDataUrl(file);
        setProgress(((i + 1) / total) * 100);
        setLibrary((lib) => {
          const next = { ...lib };
          if (!next[versionId]) next[versionId] = {};
          next[versionId][selectedSec] = [...(next[versionId][selectedSec] || []), {
            id: data.asset_id, url: preview, name: data.name, date: formatDate(new Date()), size: file.size,
          }];
          return next;
        });
      } catch (e) {
        pushToast(`Failed to upload ${file.name}: ${e.message}`, "error");
      }
    }
    setLabel("");
    setUploading(false);
    setProgress(0);
    pushToast(`${total} image${total > 1 ? "s" : ""} uploaded`, "success");
  };

  const replaceInputRef = useRefD(null);
  const [replacingImg, setReplacingImg] = useStateD(null);

  const handleReplaceClick = (img) => {
    setReplacingImg(img);
    replaceInputRef.current?.click();
  };

  const handleReplaceFile = async (file) => {
    if (!file || !replacingImg) return;
    const sectionId = selectedSection?.id;
    if (!sectionId) return;
    try {
      // Delete old
      await fetch(`${API_BASE_D}/reports/assets/${replacingImg.id}`, { method: "DELETE" });
      setLibrary((lib) => {
        const next = { ...lib };
        next[versionId][selectedSec] = (next[versionId][selectedSec] || []).filter((x) => x.id !== replacingImg.id);
        return next;
      });
      // Upload new
      const fd = new FormData();
      fd.append("image", file);
      if (label.trim()) fd.append("name", label.trim());
      const r = await fetch(`${API_BASE_D}/reports/versions/${versionId}/sections/${sectionId}/upload-image`, { method: "POST", body: fd });
      if (!r.ok) throw new Error(await r.text());
      const data = await r.json();
      const preview = await readAsDataUrl(file);
      setLibrary((lib) => {
        const next = { ...lib };
        if (!next[versionId]) next[versionId] = {};
        next[versionId][selectedSec] = [...(next[versionId][selectedSec] || []), {
          id: data.asset_id, url: preview, name: data.name, date: formatDate(new Date()),
        }];
        return next;
      });
      pushToast("Image replaced", "success");
    } catch (e) {
      pushToast(`Replace failed: ${e.message}`, "error");
    } finally {
      setReplacingImg(null);
    }
  };

  const handleDelete = (img) => setPendingDelete(img);
  const confirmDelete = async () => {
    try {
      const r = await fetch(`${API_BASE_D}/reports/assets/${pendingDelete.id}`, { method: "DELETE" });
      if (!r.ok) throw new Error(await r.text());
      setLibrary((lib) => {
        const next = { ...lib };
        next[versionId][selectedSec] = next[versionId][selectedSec].filter((x) => x.id !== pendingDelete.id);
        return next;
      });
      pushToast(`Deleted "${pendingDelete.name}"`, "info");
    } catch (e) {
      pushToast(`Delete failed: ${e.message}`, "error");
    } finally {
      setPendingDelete(null);
    }
  };

  const handleRegenerate = async () => {
    setPdfGenerating(true);
    setPdfUrl(null);
    pushToast("Generating PDF…", "info");
    try {
      const r = await fetch(`${API_BASE_D}/reports/versions/${versionId}/generate-pdf`, { method: "POST" });
      if (!r.ok) throw new Error(await r.text());
      const data = await r.json();
      const url = data.data?.download_url || data.download_url;
      setPdfUrl(url);
      pushToast("PDF ready — click Download to save", "success");
    } catch (e) {
      pushToast(`PDF generation failed: ${e.message}`, "error");
    } finally {
      setPdfGenerating(false);
    }
  };

  // Image counts per section
  const counts = useMemoD(() => {
    const m = {};
    const verLib = library[versionId] || {};
    Object.keys(verLib).forEach((num) => { m[num] = verLib[num].length; });
    return m;
  }, [library, versionId]);

  const totalImagesForVersion = Object.values(counts).reduce((a, b) => a + b, 0);
  const sectionsWithImages = Object.keys(counts).filter((n) => counts[n] > 0).length;

  const toggle = (n) => setCollapsed((s) => ({ ...s, [n]: !s[n] }));

  return (
    <div className="max-w-[1280px] mx-auto px-8 pb-10 fade-up">
      {/* Page header */}
      <div className="flex items-end justify-between mb-5">
        <div>
          <div className="mono text-[11px] uppercase tracking-[0.18em] text-teal-700 mb-2 inline-flex items-center gap-2">
            <Icons.Image size={12}/> Diagram Manager
            <span className="mono text-[10px] px-1.5 py-0.5 rounded bg-ink-100 text-ink-600 border border-ink-200">
              {totalImagesForVersion} images · {sectionsWithImages} sections
            </span>
          </div>
          <h1 className="text-[24px] font-semibold tracking-tight text-ink-900">Attach charts & diagrams</h1>
          <p className="text-[13px] text-ink-500 mt-1">Uploaded images are embedded in the matching section when the PDF is generated.</p>
        </div>
        {onBack && (
          <button onClick={onBack} className="h-9 px-3 inline-flex items-center gap-1.5 text-[12.5px] text-ink-600 border border-ink-200 rounded-md hover:bg-ink-50">
            <Icons.ArrowLeft size={13}/> Back to Generation
          </button>
        )}
      </div>

      {/* Cover image card */}
      <div className="bg-white border border-ink-100 rounded-xl shadow-card overflow-hidden mb-5">
        <div className="px-5 py-3 border-b border-ink-100 flex items-center justify-between">
          <div>
            <div className="text-[13.5px] font-semibold text-ink-900 inline-flex items-center gap-2">
              <Icons.Image size={14} className="text-teal-600"/> Report Cover Image
            </div>
            <div className="text-[12px] text-ink-500 mt-0.5">This image appears on the front page of the generated PDF.</div>
          </div>
          <span className="mono text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded bg-teal-50 text-teal-700 border border-teal-100">Report-level</span>
        </div>
        <div className="grid grid-cols-2 gap-0">
          {/* Left: upload */}
          <div className="p-5 border-r border-ink-100">
            <div
              onDragOver={(e) => e.preventDefault()}
              onDrop={(e) => { e.preventDefault(); handleCoverFiles(Array.from(e.dataTransfer.files)); }}
              onClick={() => coverInputRef.current?.click()}
              className={`cursor-pointer border-2 border-dashed rounded-lg transition-all
                ${coverUploading ? "border-teal-500 bg-teal-50/60 pointer-events-none" : "border-ink-200 hover:border-teal-400 hover:bg-teal-50/30"}`}>
              <div className="px-4 py-6 flex flex-col items-center text-center">
                <div className="h-10 w-10 rounded-full bg-teal-50 text-teal-700 grid place-items-center mb-2">
                  <Icons.UploadCloud size={18}/>
                </div>
                <div className="text-[13px] font-medium text-ink-900">Upload cover image</div>
                <div className="mono text-[10.5px] text-ink-400 mt-1">PNG, JPG · recommended 1200×800px</div>
              </div>
              <input
                ref={coverInputRef}
                type="file" accept="image/png,image/jpeg" hidden
                onChange={(e) => handleCoverFiles(Array.from(e.target.files))}/>
            </div>
            <button
              onClick={() => coverInputRef.current?.click()}
              disabled={coverUploading}
              className="mt-3 h-9 w-full inline-flex items-center justify-center gap-2 bg-teal-500 hover:bg-teal-600 disabled:bg-ink-200 disabled:text-ink-400 text-white text-[12.5px] font-medium rounded-md shadow-sm transition-colors">
              {coverUploading ? <><Icons.Loader size={13} className="spin-slow"/> Uploading…</> : <><Icons.UploadCloud size={13}/> Upload Cover</>}
            </button>
          </div>

          {/* Right: preview */}
          <div className="p-5">
            {currentCover ? (
              <>
                <div className="relative rounded-lg overflow-hidden border border-ink-100 bg-ink-50" style={{ height: 220 }}>
                  <img src={currentCover.url} alt="Cover" className="w-full h-full object-cover"/>
                  <button
                    onClick={() => coverInputRef.current?.click()}
                    disabled={coverUploading}
                    className="absolute left-1/2 -translate-x-1/2 bottom-3 h-8 px-3 inline-flex items-center gap-1.5 bg-ink-900/85 hover:bg-ink-900 text-white text-[12px] font-medium rounded-md backdrop-blur-sm">
                    <Icons.Refresh size={12}/> Replace
                  </button>
                </div>
                <div className="mono text-[11px] text-ink-400 mt-2 inline-flex items-center gap-1.5">
                  <Icons.Calendar size={10}/> Uploaded {currentCover.date}
                </div>
              </>
            ) : (
              <div className="rounded-lg border border-dashed border-ink-200 bg-ink-50/60 flex flex-col items-center justify-center text-center" style={{ height: 220 }}>
                <div className="h-10 w-10 rounded-full bg-white text-ink-300 grid place-items-center mb-2 ring-1 ring-ink-200">
                  <Icons.Image size={18}/>
                </div>
                <div className="text-[12.5px] text-ink-500 font-medium">No cover image yet</div>
                <div className="mono text-[10.5px] text-ink-400 mt-0.5">Upload on the left to add one</div>
              </div>
            )}
          </div>
        </div>
      </div>

      <div className="grid grid-cols-[340px_1fr] gap-5">
        {/* LEFT — Section selector */}
        <div className="bg-white border border-ink-100 rounded-xl shadow-card overflow-hidden flex flex-col" style={{ maxHeight: 740 }}>
          {/* Version select */}
          <div className="p-4 border-b border-ink-100">
            <label className="mono text-[10px] uppercase tracking-wider text-ink-500 mb-1.5 block">Report version</label>
            <div className="relative">
              <select
                value={versionId}
                onChange={(e) => setVersionId(e.target.value)}
                className="w-full h-9 bg-white border border-ink-200 rounded-md pl-2.5 pr-8 text-[12.5px] appearance-none focus:outline-none focus:ring-2 focus:ring-teal-500/30 focus:border-teal-500">
                {availableVersions.map((v) => (
                  <option key={v.id} value={v.id}>{v.title}</option>
                ))}
              </select>
              <Icons.ChevronDown size={13} className="absolute right-2.5 top-1/2 -translate-y-1/2 text-ink-400 pointer-events-none"/>
            </div>
            <div className="mt-1.5 mono text-[10px] text-ink-400 flex items-center gap-1.5">
              <span className="px-1.5 py-0.5 rounded bg-ink-100 text-ink-600">{versionId}</span>
              {currentVersion?.status && <Shell.StatusChip status={currentVersion.status}/>}
            </div>
          </div>

          {/* Tree header */}
          <div className="px-4 py-2.5 border-b border-ink-100 bg-ink-50/40 flex items-center justify-between">
            <span className="mono text-[10px] uppercase tracking-wider text-ink-500">Sections</span>
            <div className="flex items-center gap-1">
              <button onClick={() => setCollapsed({})} className="h-6 px-1.5 text-[10.5px] text-ink-500 hover:bg-white rounded">Expand</button>
              <button onClick={() => setCollapsed(Object.fromEntries(tree.map(c => [c.num, true])))} className="h-6 px-1.5 text-[10.5px] text-ink-500 hover:bg-white rounded">Collapse</button>
            </div>
          </div>

          <div className="flex-1 overflow-y-auto nice-scroll p-1.5">
            {tree.map((ch) => (
              <div key={ch.num}>
                <SectionTreeRow
                  num={ch.num} title={ch.title} isChapter
                  hasChildren={ch.children.length > 0}
                  collapsed={collapsed[ch.num]}
                  imageCount={counts[ch.num] || 0}
                  thumbUrl={library[versionId]?.[ch.num]?.[0]?.url || null}
                  selected={selectedSec === ch.num}
                  onClick={() => setSelectedSec(ch.num)}
                  onToggle={() => toggle(ch.num)}/>
                {!collapsed[ch.num] && ch.children.map((s) => (
                  <SectionTreeRow
                    key={s.num} num={s.num} title={s.title}
                    imageCount={counts[s.num] || 0}
                    thumbUrl={library[versionId]?.[s.num]?.[0]?.url || null}
                    selected={selectedSec === s.num}
                    onClick={() => setSelectedSec(s.num)}/>
                ))}
              </div>
            ))}
          </div>
        </div>

        {/* RIGHT — Upload + gallery */}
        <div className="space-y-4">
          {/* Breadcrumb header */}
          <div className="bg-white border border-ink-100 rounded-xl shadow-card p-5">
            <div className="flex items-center gap-1.5 text-[12.5px] text-ink-500 mb-1">
              <span className="text-ink-700 font-medium truncate max-w-[360px]">{currentVersion?.title || "Report"}</span>
              <Icons.ChevronRight size={12} className="text-ink-300"/>
              <span className="mono text-teal-700">{selectedSec}</span>
              <span className="text-ink-700">{selectedSection?.title}</span>
            </div>
            <p className="text-[12.5px] text-ink-500 leading-relaxed">
              Images uploaded here will be embedded in this section in the PDF. Drop in revenue charts, market maps, competitive matrices, or process diagrams.
            </p>
          </div>

          {/* Upload zone */}
          <div className="bg-white border border-ink-100 rounded-xl shadow-card p-5">
            <div
              onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
              onDragLeave={() => setDragging(false)}
              onDrop={(e) => {
                e.preventDefault();
                setDragging(false);
                handleFiles(Array.from(e.dataTransfer.files).filter(f => f.type.startsWith("image/")));
              }}
              onClick={() => fileInputRef.current?.click()}
              className={`relative cursor-pointer border-2 border-dashed rounded-xl transition-all
                ${dragging ? "border-teal-500 bg-teal-50/60" : "border-ink-200 hover:border-teal-400 hover:bg-teal-50/30"}
                ${uploading ? "pointer-events-none opacity-90" : ""}`}>
              <div className="p-8 flex flex-col items-center text-center">
                <div className={`h-12 w-12 rounded-full grid place-items-center mb-3 transition-colors ${dragging ? "bg-teal-500 text-white" : "bg-teal-50 text-teal-700"}`}>
                  <Icons.UploadCloud size={22}/>
                </div>
                <div className="text-[14px] font-medium text-ink-900">
                  {dragging ? "Drop to upload" : "Drag & drop charts or diagrams here"}
                </div>
                <div className="mono text-[11px] text-ink-400 mt-1.5">
                  PNG, JPG, SVG · max 10MB each · multiple files supported
                </div>
                <div className="mt-3 inline-flex items-center gap-1.5 text-[12px] text-teal-700 font-medium">
                  or click to browse <Icons.ArrowRight size={12}/>
                </div>
              </div>
              <input
                ref={fileInputRef}
                type="file" accept="image/*" multiple hidden
                onChange={(e) => handleFiles(Array.from(e.target.files))}/>
              <input
                ref={replaceInputRef}
                type="file" accept="image/*" hidden
                onChange={(e) => { if (e.target.files[0]) handleReplaceFile(e.target.files[0]); e.target.value = ""; }}/>

              {uploading && (
                <div className="absolute inset-x-4 bottom-3">
                  <div className="flex items-center justify-between mono text-[10px] text-teal-700 mb-1">
                    <span>Uploading…</span>
                    <span>{Math.round(progress)}%</span>
                  </div>
                  <div className="h-1.5 rounded-full bg-teal-100 overflow-hidden">
                    <div className="h-full bg-teal-500 transition-[width] duration-150" style={{ width: `${progress}%` }}/>
                  </div>
                </div>
              )}
            </div>

            <div className="mt-4 grid grid-cols-[1fr_auto] gap-3 items-end">
              <div>
                <label className="mono text-[10px] uppercase tracking-wider text-ink-500 mb-1 block">Image label (optional)</label>
                <input
                  value={label}
                  onChange={(e) => setLabel(e.target.value)}
                  disabled={uploading}
                  placeholder="e.g. Revenue CAGR Chart 2024–2030"
                  className="w-full h-9 bg-white border border-ink-200 rounded-md px-2.5 text-[13px] focus:outline-none focus:ring-2 focus:ring-teal-500/30 focus:border-teal-500"/>
              </div>
              <button
                onClick={() => fileInputRef.current?.click()}
                disabled={uploading}
                className="h-9 px-3.5 inline-flex items-center gap-2 bg-teal-500 hover:bg-teal-600 disabled:bg-ink-200 disabled:text-ink-400 text-white text-[12.5px] font-medium rounded-md shadow-sm transition-colors">
                {uploading ? <><Icons.Loader size={13} className="spin-slow"/> Uploading…</> : <><Icons.UploadCloud size={13}/> Upload Image</>}
              </button>
            </div>
          </div>

          {/* Gallery */}
          <div className="bg-white border border-ink-100 rounded-xl shadow-card overflow-hidden">
            <div className="px-5 py-3 border-b border-ink-100 flex items-center justify-between">
              <div className="flex items-center gap-2">
                <span className="mono text-[11px] uppercase tracking-wider text-ink-500">Uploaded images</span>
                <span className="mono text-[10px] px-1.5 py-0.5 rounded bg-teal-50 text-teal-700 border border-teal-100">
                  {sectionImages.length}
                </span>
              </div>
              {sectionImages.length > 0 && (
                <span className="mono text-[11px] text-ink-400">Embedded in section <span className="text-ink-700">{selectedSec}</span></span>
              )}
            </div>

            {sectionImages.length === 0 ? (
              <div className="p-12 text-center">
                <div className="mx-auto h-14 w-14 rounded-full bg-ink-50 grid place-items-center text-ink-300 mb-3">
                  <Icons.Image size={24}/>
                </div>
                <div className="text-[13.5px] text-ink-700 font-medium">No images yet</div>
                <div className="text-[12.5px] text-ink-400 mt-1">Upload your first diagram for this section above.</div>
              </div>
            ) : (
              <div className="p-4 grid grid-cols-2 gap-3">
                {sectionImages.map((img) => (
                  <ImageCard key={img.id} img={img} onDelete={() => handleDelete(img)} onReplace={() => handleReplaceClick(img)}/>
                ))}
              </div>
            )}

            <div className="px-5 py-3 border-t border-ink-100 bg-ink-50/40 flex items-center justify-between gap-3">
              <div className="text-[12px] text-ink-500">
                <span className="mono">{totalImagesForVersion}</span> total images across <span className="mono">{sectionsWithImages}</span> sections in this version.
              </div>
              <div className="flex items-center gap-2">
                {pdfUrl && (
                  <a
                    href={pdfUrl}
                    target="_blank"
                    rel="noreferrer"
                    className="h-9 px-3.5 inline-flex items-center gap-2 bg-emerald-600 hover:bg-emerald-700 text-white text-[12.5px] font-medium rounded-md transition-colors">
                    <Icons.Download size={13}/> Download PDF
                  </a>
                )}
                <button
                  onClick={handleRegenerate}
                  disabled={pdfGenerating}
                  className="h-9 px-3.5 inline-flex items-center gap-2 bg-ink-900 hover:bg-ink-800 disabled:bg-ink-700 text-white text-[12.5px] font-medium rounded-md transition-colors">
                  {pdfGenerating
                    ? <><Icons.Loader size={13} className="spin-slow"/> Generating…</>
                    : <><Icons.Refresh size={13}/> {pdfUrl ? "Regenerate PDF" : "Generate PDF"}</>}
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Delete confirm dialog */}
      {pendingDelete && (
        <div className="fixed inset-0 z-40 bg-ink-900/40 grid place-items-center fade-up" onClick={() => setPendingDelete(null)}>
          <div onClick={(e) => e.stopPropagation()} className="bg-white rounded-xl shadow-card-lg p-5 w-[380px]">
            <div className="flex items-start gap-3">
              <div className="h-9 w-9 rounded-full bg-red-50 text-red-600 grid place-items-center shrink-0">
                <Icons.Trash size={16}/>
              </div>
              <div className="flex-1">
                <div className="text-[14px] font-semibold text-ink-900">Delete this image?</div>
                <div className="text-[12.5px] text-ink-500 mt-1">
                  "{pendingDelete.name}" will be removed from section {selectedSec}. This cannot be undone.
                </div>
              </div>
            </div>
            <div className="mt-4 flex items-center justify-end gap-2">
              <button onClick={() => setPendingDelete(null)} className="h-9 px-3 text-[12.5px] text-ink-600 hover:bg-ink-50 rounded-md">Cancel</button>
              <button onClick={confirmDelete} className="h-9 px-3 text-[12.5px] font-medium bg-red-600 hover:bg-red-700 text-white rounded-md inline-flex items-center gap-1.5">
                <Icons.Trash size={13}/> Delete image
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function SectionTreeRow({ num, title, isChapter, hasChildren, collapsed, imageCount, thumbUrl, selected, onClick, onToggle }) {
  const hasImages = imageCount > 0;
  return (
    <div
      onClick={onClick}
      className={`group flex items-center gap-1.5 pr-2 py-1.5 rounded-md cursor-pointer transition-colors relative
        ${selected ? "bg-teal-50" : "hover:bg-ink-50"}`}
      style={{ paddingLeft: 8 + (isChapter ? 0 : 22) }}>
      {hasImages && <span className={`absolute left-0 top-1 bottom-1 w-[3px] rounded-r ${selected ? "bg-teal-600" : "bg-teal-400"}`}/>}
      {hasChildren ? (
        <button onClick={(e) => { e.stopPropagation(); onToggle && onToggle(); }} className="h-5 w-5 grid place-items-center rounded text-ink-400 hover:text-ink-700">
          <Icons.ChevronRight size={11} className={`transition-transform ${collapsed ? "" : "rotate-90"}`}/>
        </button>
      ) : (
        <span className="h-5 w-5 grid place-items-center">
          <span className="h-1 w-1 rounded-full bg-ink-300"/>
        </span>
      )}
      <span className={`mono text-[11px] tabular-nums w-10 ${isChapter ? "text-teal-700 font-semibold" : "text-ink-400"}`}>{num}</span>
      <span className={`flex-1 truncate ${isChapter ? "text-[12.5px] font-semibold text-ink-900" : "text-[12px] text-ink-700"}`}>{title}</span>
      {thumbUrl && (
        <img src={thumbUrl} alt="" className="h-6 w-6 rounded object-cover border border-ink-200 shrink-0"/>
      )}
      {hasImages && (
        <span className={`mono text-[10px] tabular-nums inline-flex items-center gap-1 px-1.5 py-0.5 rounded border shrink-0
          ${selected ? "bg-teal-100 text-teal-800 border-teal-200" : "bg-teal-50 text-teal-700 border-teal-100"}`}>
          <Icons.Image size={9}/> {imageCount}
        </span>
      )}
    </div>
  );
}

function ImageCard({ img, onDelete, onReplace }) {
  return (
    <div className="group border border-ink-100 rounded-lg overflow-hidden bg-white hover:border-ink-200 hover:shadow-card transition-all">
      <div className="relative bg-ink-50 grid place-items-center" style={{ height: 180 }}>
        <img src={img.url} alt={img.name} className="max-h-[180px] max-w-full object-contain"/>
        <button
          onClick={onReplace}
          className="absolute bottom-2 left-1/2 -translate-x-1/2 h-7 px-2.5 inline-flex items-center gap-1.5 bg-ink-900/80 hover:bg-ink-900 text-white text-[11px] font-medium rounded-md backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity">
          <Icons.Refresh size={11}/> Replace
        </button>
      </div>
      <div className="px-3 py-2.5 flex items-center gap-2">
        <div className="min-w-0 flex-1">
          <div className="text-[12.5px] font-medium text-ink-900 truncate">{img.name}</div>
          <div className="mono text-[10.5px] text-ink-400 flex items-center gap-1.5 mt-0.5">
            <Icons.Calendar size={9}/> {img.date}
            {img.size && <><span>·</span><span>{(img.size / 1024).toFixed(0)}KB</span></>}
          </div>
        </div>
        <button
          onClick={onDelete}
          className="h-7 w-7 grid place-items-center text-ink-300 hover:text-red-600 hover:bg-red-50 rounded-md transition-colors"
          aria-label="Delete image">
          <Icons.Trash size={13}/>
        </button>
      </div>
    </div>
  );
}

// ——— helpers
function buildTreeD(sections) {
  const out = [];
  let current = null;
  for (const s of sections) {
    if (!s.num.includes(".")) {
      current = { ...s, children: [] };
      out.push(current);
    } else if (current) {
      current.children.push(s);
    }
  }
  return out;
}

function readAsDataUrl(file) {
  return new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(r.result);
    r.onerror = reject;
    r.readAsDataURL(file);
  });
}

function formatDate(d) {
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
}


window.DiagramManager = DiagramManager;
