/* ============================================================
   canvas.jsx — A4 paginated canvas with live flow + drop zones
   ============================================================ */

function Canvas() {
  const { doc, addBlock, moveBlock, setSelectedBlockId, tweaks, updateMeta } = useApp();
  const [dropIdx, setDropIdx] = useState(null);
  const [dragKind, setDragKind] = useState(null); // 'new' | 'move' | null
  const canvasRef = useRef(null);

  const pageStyle = {
    '--doc-font-size': doc.meta.fontSize + 'px',
    '--doc-body-font': `"${doc.meta.bodyFont}", "Geist", sans-serif`,
    '--doc-head-font': `"${doc.meta.headFont}", "Geist", sans-serif`,
    '--doc-margin': doc.meta.margin,
    '--accent': doc.meta.accent,
    '--doc-columns': doc.meta.columns || 1,
  };

  const isLandscape = doc.meta.orientation === 'landscape';
  const columns     = doc.meta.columns || 1;

  // inject heading styles into <head> (side-effect only; returns null)
  useMemo(() => {
    const s = doc.headingStyles || {};
    const style = document.getElementById('dyn-heading-style') || (() => {
      const el = document.createElement('style'); el.id = 'dyn-heading-style'; document.head.appendChild(el); return el;
    })();
    const cssFor = (sel, cfg, baseEm) => {
      if (!cfg) return '';
      const color = cfg.color === 'accent' ? 'var(--accent)' : cfg.color === 'muted' ? 'var(--ink-3)' : 'var(--ink)';
      const tt = (cfg.case === 'upper' || cfg.case === 'small') ? 'uppercase' : 'none';
      const ls = (cfg.case === 'small' || cfg.case === 'upper') ? '0.08em' : 'normal';
      const fs = cfg.case === 'small'
        ? `calc(var(--doc-font-size) * 0.92 * ${cfg.size})`
        : `calc(var(--doc-font-size) * ${baseEm} * ${cfg.size})`;
      return `${sel}{color:${color};text-transform:${tt};letter-spacing:${ls};font-size:${fs};font-weight:${cfg.weight};}`;
    };
    style.textContent = [
      cssFor('.page h1.doc-h1', s.h1, 2.1),
      cssFor('.page h2.doc-h2', s.h2, 1.45),
      cssFor('.page h3.doc-h3', s.h3, 1.1),
    ].join('\n');
    return null;
  }, [doc.headingStyles]);

  /* ---------- pagination ---------- */
  const pages = usePaginatedBlocks(doc, columns);

  /* ---------- drag-and-drop ---------- */
  // block index (from paginated view) → linear index in doc.blocks
  const blockIndexById = useMemo(() => {
    const m = new Map();
    doc.blocks.forEach((b, i) => m.set(b.id, i));
    return m;
  }, [doc.blocks]);

  const onDragOverGap = (e, i) => {
    e.preventDefault();
    setDropIdx(i);
  };
  const onDrop = (e) => {
    e.preventDefault();
    const newType = e.dataTransfer.getData('text/block-new');
    const move    = e.dataTransfer.getData('text/block-move');
    const at = dropIdx == null ? doc.blocks.length : dropIdx;
    if (newType) addBlock(newType, at);
    else if (move) moveBlock(Number(move), at);
    setDropIdx(null);
    setDragKind(null);
  };
  const onDragEnd = () => { setDropIdx(null); setDragKind(null); };

  /* expose drag-begin so sidebar can trigger dragKind state */
  useEffect(() => {
    const start = (e) => {
      const t = e.detail?.kind;
      if (t) setDragKind(t);
    };
    const end = () => { setDropIdx(null); setDragKind(null); };
    window.addEventListener('tender-drag-start', start);
    window.addEventListener('tender-drag-end', end);
    return () => {
      window.removeEventListener('tender-drag-start', start);
      window.removeEventListener('tender-drag-end', end);
    };
  }, []);

  const isPrint = tweaks.previewMode === 'print';

  return (
    <div
      className={'canvas-wrap' + (isPrint ? ' print-mode' : '') + (dragKind ? ' is-dragging' : '')}
      onClick={() => setSelectedBlockId(null)}
      onDragOver={(e) => { if (doc.blocks.length === 0) { e.preventDefault(); setDropIdx(0); } }}
      onDrop={onDrop}
      onDragEnd={onDragEnd}
      ref={canvasRef}
    >
      {pages.length === 0 && (
        <PageShell meta={doc.meta} pageStyle={pageStyle} isLandscape={isLandscape} pageNum={1} totalPages={1}>
          <EmptyState/>
        </PageShell>
      )}

      {pages.map((page, pageIdx) => (
        <PageShell
          key={pageIdx}
          meta={doc.meta}
          pageStyle={pageStyle}
          isLandscape={isLandscape}
          pageNum={pageIdx + 1}
          totalPages={pages.length}
        >
          <div className="page-columns" data-col-count={columns}
               style={{
                 display: 'grid',
                 gridTemplateColumns: `repeat(${columns}, 1fr)`,
                 columnGap: columns > 1 ? 28 : 0,
                 height: '100%',
                 minHeight: 0,
               }}>
            {page.columns.map((col, colIdx) => (
              <div className="page-col" key={colIdx}
                   style={{
                     minWidth: 0,
                     borderLeft: colIdx > 0 ? '1px solid var(--rule)' : 'none',
                     paddingLeft: colIdx > 0 ? 14 : 0,
                   }}>
                {col.length === 0 && colIdx > 0 && (
                  <div className="col-empty-hint" style={{
                    height: '100%', display:'flex', alignItems:'center', justifyContent:'center',
                    color:'var(--ink-4)', fontSize: 11, fontStyle:'italic', opacity: .5,
                  }}>
                    vervolg kolom
                  </div>
                )}
                {col.map((item) => {
                  const linearIdx = blockIndexById.get(item.id);
                  if (linearIdx == null) return null; // defensive
                  return (
                    <BlockWithDropzones
                      key={item.id}
                      block={item.block}
                      index={linearIdx}
                      dropIdx={dropIdx}
                      dragKind={dragKind}
                      onDragOverGap={onDragOverGap}
                      onDrop={onDrop}
                    />
                  );
                })}
                {/* trailing drop zone: only on last column of last page */}
                {pageIdx === pages.length - 1 && colIdx === page.columns.length - 1 && (
                  <DropZone
                    i={doc.blocks.length}
                    active={dropIdx === doc.blocks.length}
                    visible={!!dragKind}
                    onDragOver={onDragOverGap}
                    onDrop={onDrop}
                    trailing
                  />
                )}
              </div>
            ))}
          </div>
        </PageShell>
      ))}

      <SelectionToolbar containerRef={canvasRef}/>
    </div>
  );
}

/* ------------------------------------------------------------
   PageShell — one A4 sheet with header + footer + content slot
   ------------------------------------------------------------ */
function PageShell({ meta, pageStyle, isLandscape, pageNum, totalPages, children }) {
  return (
    <div className={'page a4' + (isLandscape ? ' landscape' : '')}
         data-columns={meta.columns || 1}
         data-page-num={pageNum}
         style={pageStyle}
         onClick={(e) => e.stopPropagation()}>
      {meta.showHeader && (
        <div className="page-header">
          <div className="logo-slot">
            {meta.logoUrl
              ? <img className="logo-img" src={meta.logoUrl} alt="logo"/>
              : <span className="logo-placeholder">LOGO</span>}
            <span>{meta.orgName}</span>
          </div>
          <div>{meta.headerText}</div>
        </div>
      )}

      <div className="page-content">
        {children}
      </div>

      {meta.showFooter && (
        <div className="page-footer">
          <span>{(meta.footerText || '').replace('{page}', pageNum).replace('{total}', totalPages)}</span>
          <span>{meta.orgName}</span>
        </div>
      )}

      <div className="page-badge" aria-hidden="true">pag. {pageNum} / {totalPages}</div>
    </div>
  );
}

/* ------------------------------------------------------------
   usePaginatedBlocks — measure + paginate, debounced, memoized
   ------------------------------------------------------------ */
function usePaginatedBlocks(doc, columns) {
  const [pages, setPages] = useState([]);
  const timerRef = useRef(null);

  // recompute when blocks / meta / columns change
  useEffect(() => {
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      // defensive: paginate.jsx may not have loaded yet (script order changes)
      if (typeof window.measureBlocks !== 'function' ||
          typeof window.paginate       !== 'function' ||
          typeof window.computeContentHeight !== 'function') {
        console.warn('[canvas] paginate helpers not ready yet — skipping');
        // retry once after next tick
        setTimeout(() => setPages(prev => [...prev]), 80);
        return;
      }
      try {
        const pageStyle = {
          '--doc-font-size': doc.meta.fontSize + 'px',
          '--doc-body-font': `"${doc.meta.bodyFont}", "Geist", sans-serif`,
          '--doc-head-font': `"${doc.meta.headFont}", "Geist", sans-serif`,
          '--doc-margin': doc.meta.margin,
          '--accent': doc.meta.accent,
        };
        const colWidth = window.computeContentWidth(doc.meta, columns);
        const measured = window.measureBlocks(doc.blocks, pageStyle, colWidth);
        const contentH = window.computeContentHeight(doc.meta);
        const paged = window.paginate(measured, {
          contentHeight: contentH,
          columns,
        });
        if (window.TENDER_DEBUG_PAGINATION) {
          console.log('[paginate]', {
            blocks: doc.blocks.length,
            pages: paged.length,
            contentH, colWidth, columns,
            measured: measured.map(m => ({id: m.id, type: m.block.type, h: Math.round(m.height)})),
          });
        }
        setPages(paged);
      } catch (err) {
        console.error('[canvas] pagination failed', err);
        // fallback: render everything in a single "page" with all blocks in col 0
        setPages([{
          columns: [doc.blocks.map(b => ({ id: b.id, block: b, height: 0 }))].concat(
            Array.from({length: Math.max(0, columns - 1)}, () => [])
          ),
          usedHeight: Array.from({length: columns}, () => 0),
        }]);
      }
    }, 120);
    return () => clearTimeout(timerRef.current);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    doc.blocks,
    doc.meta.fontSize, doc.meta.bodyFont, doc.meta.headFont,
    doc.meta.margin, doc.meta.orientation, doc.meta.showHeader, doc.meta.showFooter,
    columns,
  ]);

  return pages;
}

/* ------------------------------------------------------------
   BlockWithDropzones — a block + leading + trailing drop zone
   ------------------------------------------------------------ */
function BlockWithDropzones({ block, index, dropIdx, dragKind, onDragOverGap, onDrop }) {
  return (
    <>
      <DropZone
        i={index}
        active={dropIdx === index}
        visible={!!dragKind}
        onDragOver={onDragOverGap}
        onDrop={onDrop}
      />
      <BlockRenderer block={block} index={index}/>
    </>
  );
}

function DropZone({ i, active, visible, onDragOver, onDrop, trailing }) {
  return (
    <div
      className={
        'drop-zone' +
        (visible ? ' visible' : '') +
        (active ? ' active' : '') +
        (trailing ? ' trailing' : '')
      }
      onDragOver={(e) => onDragOver(e, i)}
      onDrop={onDrop}
    >
      <div className="drop-zone-bar"/>
    </div>
  );
}

/* ------------------------------------------------------------
   EmptyState — first-use affordance
   ------------------------------------------------------------ */
function EmptyState() {
  const { loadStarter, addBlock } = useApp();
  return (
    <div style={{textAlign:'center', padding: '40px 20px 20px', color: 'var(--ink-4)'}}>
      <div style={{fontFamily:'var(--f-serif)', fontStyle:'italic', fontSize: 28, color:'var(--ink-3)', marginBottom: 8}}>
        Leeg canvas
      </div>
      <p style={{fontSize: 13, lineHeight: 1.5, maxWidth: 360, margin:'0 auto 18px'}}>
        Sleep een bouwblok uit de zijbalk naar deze pagina,<br/>
        of voeg er snel eentje toe om te beginnen. Dubbelklik werkt ook.
      </p>
      <div style={{display:'flex', gap: 6, justifyContent:'center', flexWrap:'wrap'}}>
        <button className="btn btn-primary" onClick={() => addBlock('h1')}>Titel toevoegen</button>
        <button className="btn" onClick={() => addBlock('p')}>Paragraaf</button>
        <button className="btn" onClick={loadStarter}>Voorbeeld laden</button>
      </div>
    </div>
  );
}

/* ------------------------------------------------------------
   floating selection toolbar — comments / suggestions
   ------------------------------------------------------------ */
function SelectionToolbar({ containerRef }) {
  const { addComment, addSuggestion, tweaks } = useApp();
  const [sel, setSel] = useState(null);

  useEffect(() => {
    const onUp = () => {
      const s = window.getSelection();
      if (!s || s.isCollapsed) { setSel(null); return; }
      const range = s.getRangeAt(0);
      let node = range.commonAncestorContainer;
      while (node && node.nodeType !== 1) node = node.parentNode;
      const blockEl = node?.closest?.('[data-block-field]');
      if (!blockEl) { setSel(null); return; }
      const blockId = blockEl.getAttribute('data-block-id');
      const field   = blockEl.getAttribute('data-block-field');
      const fullText = blockEl.textContent || '';
      const selText  = s.toString();
      if (!selText.trim()) { setSel(null); return; }
      const start = fullText.indexOf(selText);
      if (start < 0) { setSel(null); return; }
      const rect = range.getBoundingClientRect();
      const cRect = containerRef.current.getBoundingClientRect();
      setSel({
        blockId, field, start, end: start + selText.length, text: selText,
        x: rect.left + rect.width / 2 - cRect.left + containerRef.current.scrollLeft,
        y: rect.top - cRect.top + containerRef.current.scrollTop,
      });
    };
    document.addEventListener('mouseup', onUp);
    document.addEventListener('keyup', onUp);
    return () => {
      document.removeEventListener('mouseup', onUp);
      document.removeEventListener('keyup', onUp);
    };
  }, []);

  const roleMeta = ROLES[tweaks.role] || ROLES.instromer;
  const { currentAuthor } = useApp();
  const author = currentAuthor();
  const canSuggest = roleMeta.canSuggest;

  const doComment = () => {
    const text = prompt(`Opmerking bij: "${sel.text.slice(0,60)}${sel.text.length>60?'…':''}"`);
    if (text) addComment({ blockId: sel.blockId, range:{field: sel.field, start: sel.start, end: sel.end}, text, author: author.name, color: author.color });
    setSel(null); window.getSelection().removeAllRanges();
  };
  const doSuggest = () => {
    const proposed = prompt(`Voorgestelde vervanging voor: "${sel.text}"`, sel.text);
    if (proposed != null && proposed !== sel.text) {
      addSuggestion({ blockId: sel.blockId, range:{field: sel.field, start: sel.start, end: sel.end}, original: sel.text, proposed, author: author.name, color: author.color });
    }
    setSel(null); window.getSelection().removeAllRanges();
  };

  if (!sel) return null;
  return (
    <div className="sel-toolbar" style={{left: sel.x, top: sel.y}}>
      <button onClick={doComment}><I.comment/> Comment</button>
      {canSuggest && <>
        <div className="sep"/>
        <button onClick={doSuggest}><I.edit/> Voorstel</button>
      </>}
    </div>
  );
}

const ROLES = {
  instromer:    { name: 'Marit de Vries', color: '#8b9a54', canSuggest: true, label: 'Instromer' },
  reviewer:     { name: 'Jeroen Haga',    color: '#b97550', canSuggest: true, label: 'Reviewer' },
  eindredacteur:{ name: 'Anouk Vermeer',  color: '#4a6fa5', canSuggest: true, label: 'Eindredacteur' },
};

Object.assign(window, { Canvas, ROLES });
