// Page / Section / Content row components

const { useState: useStateN, useEffect: useEffectN, useMemo: useMemoN, useCallback: useCallbackN } = React;

// ---------- Drag context (simple module-scope mediator) ----------
const dragState = {
  current: null,  // { id, parentId, type }
  onDrop: null,   // (fromId, toId, before) => void
};

function startDrag(e, id, parentId, type) {
  dragState.current = { id, parentId, type };
  try {
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/plain", id);
  } catch (_) {}
}
function endDrag() { dragState.current = null; }
function canDropOn(targetParentId, targetType) {
  const d = dragState.current;
  return d && d.parentId === targetParentId && d.type === targetType;
}

// ---------- Status badge ----------
function StatusBadge({ status }) {
  if (!status) return null;
  return (
    <span className={`badge ${status}`}>
      <span className="dot"></span>
      {window.statusLabel(status)}
    </span>
  );
}

function CommentBadge({ count, onClick }) {
  return (
    <button
      className={`comment-badge ${count > 0 ? "has" : ""}`}
      onClick={onClick}
      title={count === 0 ? "No comments yet" : `${count} comment${count > 1 ? "s" : ""}`}
    >
      <window.CommentGlyph />
      {count}
    </button>
  );
}

// ---------- ContentRow ----------
function ContentRow({ node, selected, onSelect, parentId, dragOver, onDragOverItem, onDropItem, dimmed, suggestionCount, funcCount, commentCount }) {
  const handleDragStart = (e) => startDrag(e, node.id, parentId, "content");
  const handleDragOver = (e) => {
    if (canDropOn(parentId, "content")) {
      e.preventDefault();
      onDragOverItem(node.id);
    }
  };
  const handleDrop = (e) => {
    if (canDropOn(parentId, "content")) {
      e.preventDefault();
      onDropItem(node.id);
    }
  };

  return (
    <div
      id={`node-${node.id}`}
      className={`content ${selected ? "selected" : ""} ${dragOver ? "drop-target" : ""} ${dimmed ? "dim" : ""}`}
      onClick={(e) => { e.stopPropagation(); onSelect(node.id); }}
      draggable
      onDragStart={handleDragStart}
      onDragEnd={endDrag}
      onDragOver={handleDragOver}
      onDrop={handleDrop}
    >
      <span className="content-dot"></span>
      <span className="content-text">{node.title}</span>
      {(suggestionCount > 0 || funcCount > 0 || commentCount > 0) && (
        <span className="content-pips">
          {suggestionCount > 0 && (
            <span className="content-pip pip-sg" title={`${suggestionCount} content draft${suggestionCount !== 1 ? "s" : ""}`}>
              <svg width="9" height="9" viewBox="0 0 12 12" aria-hidden="true">
                <rect x="2.5" y="1.5" width="6" height="8" rx="0.6" fill="none" stroke="currentColor" strokeWidth="1.1" />
                <rect x="3.5" y="3.5" width="6" height="8" rx="0.6" fill="currentColor" opacity="0.25" stroke="currentColor" strokeWidth="1.1" />
              </svg>
              {suggestionCount}
            </span>
          )}
          {funcCount > 0 && (
            <span className="content-pip pip-fn" title={`${funcCount} functionality note${funcCount !== 1 ? "s" : ""}`}>
              <svg width="9" height="9" viewBox="0 0 12 12" aria-hidden="true">
                <circle cx="6" cy="6" r="4.5" fill="none" stroke="currentColor" strokeWidth="1.2" />
                <path d="M5.5 4.5 L7.5 6 L5.5 7.5 Z" fill="currentColor" />
              </svg>
              {funcCount}
            </span>
          )}
        </span>
      )}
    </div>
  );
}

// ---------- SectionRow ----------
function SectionRow({ node, children, childOrder, nodes, isOpen, onToggle, selectedId, onSelect, commentsByNode, parentId, dragOver, onDragOverItem, onDropItem, search, typeFilter, statusFilter, commentOnly, suggestionsByContent, functionalityByContent, onAddContent }) {
  const childContentIds = childOrder[node.id] || [];

  const handleDragStart = (e) => { e.stopPropagation(); startDrag(e, node.id, parentId, "section"); };
  const handleDragOver = (e) => {
    if (canDropOn(parentId, "section")) {
      e.preventDefault();
      onDragOverItem(node.id);
    }
  };
  const handleDrop = (e) => {
    if (canDropOn(parentId, "section")) {
      e.preventDefault();
      onDropItem(node.id);
    }
  };

  const [contentDragOverId, setContentDragOverId] = useStateN(null);

  // Filter content items
  const visibleContent = childContentIds.filter((cid) => {
    const c = nodes[cid];
    if (typeFilter.size && !typeFilter.has("content")) return false;
    if (search && !c.title.toLowerCase().includes(search)) return false;
    return true;
  });

  const matchesSearch = !search ||
    node.title.toLowerCase().includes(search) ||
    (node.summary || "").toLowerCase().includes(search) ||
    childContentIds.some((cid) => nodes[cid].title.toLowerCase().includes(search));
  const typeAllowed = !typeFilter.size || typeFilter.has("section") || typeFilter.has("content");
  const statusAllowed = !statusFilter.size || statusFilter.has(node.status);
  if (!matchesSearch || !typeAllowed || !statusAllowed) return null;
  if (commentOnly) return null; // sections don't have direct comments in this dataset

  return (
    <div
      id={`node-${node.id}`}
      className={`section ${selectedId === node.id ? "selected" : ""} ${dragOver ? "drop-target" : ""}`}
      draggable
      onDragStart={handleDragStart}
      onDragEnd={endDrag}
      onDragOver={handleDragOver}
      onDrop={handleDrop}
    >
      <div className="section-head" onClick={(e) => { e.stopPropagation(); onSelect(node.id); onToggle(node.id); }}>
        <span className="drag-handle" onClick={(e) => e.stopPropagation()}><window.DragDots /></span>
        <button className={`expand-toggle ${isOpen ? "open" : ""}`} onClick={(e) => { e.stopPropagation(); onToggle(node.id); }}>
          <window.Chevron open={isOpen} />
        </button>
        <span className="type-glyph"><window.Glyph type="section" /></span>
        <div className="section-title">
          <h4>{node.title}</h4>
          {node.summary && !isOpen && <span className="section-summary-inline">— {node.summary}</span>}
        </div>
        <span className="counts">
          {childContentIds.length} item{childContentIds.length !== 1 ? "s" : ""}
          {node.status && node.status !== "mvp" && <> · <StatusBadge status={node.status} /></>}
        </span>
      </div>
      {isOpen && (
        <div className="contents">
          {visibleContent.map((cid) => (
            <ContentRow
              key={cid}
              node={nodes[cid]}
              selected={selectedId === cid}
              onSelect={onSelect}
              parentId={node.id}
              dragOver={contentDragOverId === cid}
              suggestionCount={(suggestionsByContent?.[cid] || []).length}
              funcCount={(functionalityByContent?.[cid] || []).length}
              onDragOverItem={setContentDragOverId}
              onDropItem={(toId) => {
                const from = dragState.current?.id;
                if (from) dragState.onDrop?.(from, toId, false);
                setContentDragOverId(null);
                endDrag();
              }}
            />
          ))}
          {visibleContent.length === 0 && (
            <div style={{ gridColumn: "1/-1", fontSize: 12, color: "var(--muted)", fontStyle: "italic", padding: "4px 0" }}>
              No content items match the current filter.
            </div>
          )}
          <button
            className="add-inline-btn add-content-btn"
            style={{ gridColumn: "1/-1" }}
            onClick={(e) => { e.stopPropagation(); onAddContent(node.id); }}
            title="Add a content item to this section"
          >
            <span className="add-glyph">+</span>
            Add content item
          </button>
        </div>
      )}
    </div>
  );
}

// ---------- PageCard ----------
function PageCard({ node, childOrder, nodes, openMap, onToggle, selectedId, onSelect, commentsByNode, parentId, dragOver, onDragOverItem, onDropItem, search, typeFilter, statusFilter, commentOnly, suggestionsByContent, functionalityByContent, onAddSection, onAddFoldItem, onAddContent }) {
  const sectionIds = childOrder[node.id] || [];
  const isOpen = openMap[node.id] !== false; // default open
  const [sectionDragOverId, setSectionDragOverId] = useStateN(null);

  const handleDragStart = (e) => startDrag(e, node.id, parentId, "page");
  const handleDragOver = (e) => {
    if (canDropOn(parentId, "page")) {
      e.preventDefault();
      onDragOverItem(node.id);
    }
  };
  const handleDrop = (e) => {
    if (canDropOn(parentId, "page")) {
      e.preventDefault();
      onDropItem(node.id);
    }
  };

  // Visibility computation incl. descendants
  const contentCount = sectionIds.reduce((s, sid) => s + (childOrder[sid]?.length || 0), 0);
  const commentCount = (commentsByNode[node.id] || []).length;

  // Does the current selection live inside this page?
  let hasSelectedDescendant = false;
  if (selectedId && selectedId !== node.id) {
    const sel = nodes[selectedId];
    if (sel) {
      if (sel.type === "section" && sel.parentId === node.id) hasSelectedDescendant = true;
      else if (sel.type === "content" && sel.pageId === node.id) hasSelectedDescendant = true;
    }
  }

  // Visibility considering search + filters
  const titleHit = !search || node.title.toLowerCase().includes(search) || node.url.toLowerCase().includes(search) || (node.summary || "").toLowerCase().includes(search);
  let hasMatchingDescendant = titleHit;
  if (!hasMatchingDescendant && search) {
    for (const sid of sectionIds) {
      const sec = nodes[sid];
      if (sec.title.toLowerCase().includes(search) || (sec.summary || "").toLowerCase().includes(search)) {
        hasMatchingDescendant = true; break;
      }
      for (const cid of childOrder[sid] || []) {
        if (nodes[cid].title.toLowerCase().includes(search)) { hasMatchingDescendant = true; break; }
      }
      if (hasMatchingDescendant) break;
    }
  }
  if (!hasMatchingDescendant) return null;

  // Type filter — if active and 'page' isn't included BUT descendants might be, we still show the page as a container
  // Only hide if filter excludes pages AND no descendant types are included
  if (typeFilter.size) {
    const showPage = typeFilter.has("page") || typeFilter.has("section") || typeFilter.has("content");
    if (!showPage) return null;
  }

  // Status filter — show if page or any section matches
  if (statusFilter.size) {
    const pageOk = statusFilter.has(node.status);
    const secOk = sectionIds.some((sid) => statusFilter.has(nodes[sid].status));
    if (!pageOk && !secOk) return null;
  }

  // Comments-only filter
  if (commentOnly && commentCount === 0) return null;

  return (
    <article
      id={`node-${node.id}`}
      className={`page ${selectedId === node.id ? "selected" : ""} ${hasSelectedDescendant ? "has-selected-child" : ""} ${dragOver ? "drop-target" : ""}`}
      draggable
      onDragStart={handleDragStart}
      onDragEnd={endDrag}
      onDragOver={handleDragOver}
      onDrop={handleDrop}
    >
      <header className="page-head" onClick={(e) => { e.stopPropagation(); onSelect(node.id); }}>
        <span className="drag-handle" onClick={(e) => e.stopPropagation()} title="Drag to reorder"><window.DragDots /></span>
        <button
          className={`expand-toggle ${isOpen ? "open" : ""}`}
          onClick={(e) => { e.stopPropagation(); onToggle(node.id); }}
          aria-label={isOpen ? "Collapse page" : "Expand page"}
        >
          <window.Chevron open={isOpen} />
        </button>
        <span className="type-glyph"><window.Glyph type="page" /></span>
        <div className="page-title">
          <h3>{node.title}</h3>
          <span className="page-url">{node.url}</span>
          {hasSelectedDescendant && !isOpen && (
            <span
              className="nested-selection-tag"
              title={`Selected inside: ${nodes[selectedId].title}`}
              onClick={(e) => { e.stopPropagation(); onToggle(node.id); }}
            >
              <svg width="9" height="9" viewBox="0 0 10 10" aria-hidden="true">
                <circle cx="5" cy="5" r="2.5" fill="currentColor" />
              </svg>
              Selection inside
            </span>
          )}
        </div>
        <div className="page-badges">
          <span className="counts">
            {sectionIds.length} section{sectionIds.length !== 1 ? "s" : ""} · {contentCount} item{contentCount !== 1 ? "s" : ""}
          </span>
          <StatusBadge status={node.status} />
          <CommentBadge count={commentCount} onClick={(e) => { e.stopPropagation(); onSelect(node.id); }} />
        </div>
      </header>

      {isOpen && (
        <div className="page-body">
          {node.summary && (
            <p className="page-summary"><em>{node.title} —</em> {node.summary}</p>
          )}

          {(() => {
            const foldIds = childOrder[`foldlist:${node.id}`] || [];
            if (foldIds.length === 0) {
              return (
                <div className="fold-zone fold-zone-empty">
                  <div className="fold-zone-head">
                    <span className="fold-zone-label">Above the fold</span>
                    <span className="fold-zone-caption">No items yet — define what a visitor should see before scrolling.</span>
                  </div>
                  <button
                    className="add-inline-btn add-fold-btn"
                    onClick={(e) => { e.stopPropagation(); onAddFoldItem(node.id); }}
                  >
                    <span className="add-glyph">+</span>
                    Add above-the-fold item
                  </button>
                </div>
              );
            }
            return (
              <div className="fold-zone">
                <div className="fold-zone-head">
                  <span className="fold-zone-label">Above the fold</span>
                  <span className="fold-zone-caption">What a visitor should see before scrolling · click an item to inspect</span>
                </div>
                <ol className="fold-list">
                  {foldIds.map((foldId, i) => {
                    const fn = nodes[foldId];
                    if (!fn) return null;
                    const isSel = selectedId === foldId;
                    const sgCount = (suggestionsByContent?.[foldId] || []).length;
                    const fnCount = (functionalityByContent?.[foldId] || []).length;
                    return (
                      <li
                        key={foldId}
                        id={`node-${foldId}`}
                        className={`fold-item ${isSel ? "selected" : ""}`}
                        onClick={(e) => { e.stopPropagation(); onSelect(foldId); }}
                        role="button"
                        tabIndex={0}
                        onKeyDown={(e) => {
                          if (e.key === "Enter" || e.key === " ") {
                            e.preventDefault(); onSelect(foldId);
                          }
                        }}
                      >
                        <span className="fold-num">{String(i + 1).padStart(2, "0")}</span>
                        <span className="fold-text">{fn.title}</span>
                        {(sgCount > 0 || fnCount > 0) && (
                          <span className="fold-pips">
                            {sgCount > 0 && (
                              <span className="content-pip pip-sg" title={`${sgCount} draft${sgCount !== 1 ? "s" : ""}`}>
                                <svg width="9" height="9" viewBox="0 0 12 12" aria-hidden="true">
                                  <rect x="2.5" y="1.5" width="6" height="8" rx="0.6" fill="none" stroke="currentColor" strokeWidth="1.1" />
                                  <rect x="3.5" y="3.5" width="6" height="8" rx="0.6" fill="currentColor" opacity="0.25" stroke="currentColor" strokeWidth="1.1" />
                                </svg>
                                {sgCount}
                              </span>
                            )}
                            {fnCount > 0 && (
                              <span className="content-pip pip-fn" title={`${fnCount} functionality note${fnCount !== 1 ? "s" : ""}`}>
                                <svg width="9" height="9" viewBox="0 0 12 12" aria-hidden="true">
                                  <circle cx="6" cy="6" r="4.5" fill="none" stroke="currentColor" strokeWidth="1.2" />
                                  <path d="M5.5 4.5 L7.5 6 L5.5 7.5 Z" fill="currentColor" />
                                </svg>
                                {fnCount}
                              </span>
                            )}
                          </span>
                        )}
                      </li>
                    );
                  })}
                </ol>
                <button
                  className="add-inline-btn add-fold-btn"
                  onClick={(e) => { e.stopPropagation(); onAddFoldItem(node.id); }}
                >
                  <span className="add-glyph">+</span>
                  Add above-the-fold item
                </button>
                <div className="fold-line" aria-hidden="true">
                  <span className="fold-line-glyph">▾</span>
                  <span className="fold-line-label">the fold</span>
                  <span className="fold-line-glyph">▾</span>
                </div>
              </div>
            );
          })()}

          <div className="sections">
            {sectionIds.map((sid) => (
              <SectionRow
                key={sid}
                node={nodes[sid]}
                childOrder={childOrder}
                nodes={nodes}
                isOpen={openMap[sid] === true}
                onToggle={onToggle}
                selectedId={selectedId}
                onSelect={onSelect}
                commentsByNode={commentsByNode}
                parentId={node.id}
                dragOver={sectionDragOverId === sid}
                onDragOverItem={setSectionDragOverId}
                suggestionsByContent={suggestionsByContent}
                functionalityByContent={functionalityByContent}
                onAddContent={onAddContent}
                onDropItem={(toId) => {
                  const from = dragState.current?.id;
                  if (from) dragState.onDrop?.(from, toId, false);
                  setSectionDragOverId(null);
                  endDrag();
                }}
                search={search}
                typeFilter={typeFilter}
                statusFilter={statusFilter}
                commentOnly={commentOnly}
              />
            ))}
            <button
              className="add-inline-btn add-section-btn"
              onClick={(e) => { e.stopPropagation(); onAddSection(node.id); }}
              title="Add a section to this page"
            >
              <span className="add-glyph">+</span>
              Add section
            </button>
          </div>
        </div>
      )}
    </article>
  );
}

Object.assign(window, {
  PageCard, SectionRow, ContentRow, StatusBadge, CommentBadge,
  dragState, startDrag, endDrag, canDropOn,
});
