// tc-annotate.jsx — free-text annotation layer.
// Lets anyone click any blank spot on the form preview and type. Boxes are
// draggable, editable, resizable, deletable, and are persisted on the card/kit
// (card.annotations / kit.annotations) so they also print into the exported PDF.
//
// Coordinates are stored in FORM units (top-left origin, same px space as the
// draw-ops + the PDF transform): x = left, y = top of the text box, size = font px.
// The layer renders at the form's native size inside the (CSS-scaled) overlay, so
// we convert pointer coords with the layer's own getBoundingClientRect scale.

const { useState: useAS, useRef: useAR, useEffect: useAE, useCallback: useACB } = React;

const ANNOT_MIN = 7, ANNOT_MAX = 30, ANNOT_DEFAULT = 13;

function annotUid() { return 'an_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5); }

// One editable note.
function AnnotItem({ item, ink, active, selected, onSelect, onDone, onChange, onRemove, toFormCoords, autoFocus, uiScale }) {
  const taRef = useAR(null);

  const autosize = useACB(() => {
    const ta = taRef.current; if (!ta) return;
    ta.style.width = '0px'; ta.style.height = '0px';
    ta.style.width = Math.max(14, ta.scrollWidth) + 'px';
    ta.style.height = ta.scrollHeight + 'px';
  }, []);

  useAE(() => { autosize(); }, [item.text, item.size, autosize]);
  useAE(() => {
    if (!autoFocus) return;
    const focusNow = () => {
      const ta = taRef.current;
      if (ta) { ta.focus({ preventScroll: true }); try { ta.select(); } catch (e) {} }
    };
    // rAF (after layout) + a timeout fallback for Chrome focus timing
    const raf = requestAnimationFrame(focusNow);
    const tid = setTimeout(focusNow, 60);
    return () => { cancelAnimationFrame(raf); clearTimeout(tid); };
  }, [autoFocus]);

  // drag the box by its grip
  const onGripDown = (e) => {
    e.preventDefault(); e.stopPropagation();
    onSelect();
    const start = toFormCoords(e);
    const ox = start.x - item.x, oy = start.y - item.y;
    const move = (ev) => {
      const p = toFormCoords(ev);
      onChange({ ...item, x: Math.round(p.x - ox), y: Math.round(p.y - oy) });
    };
    const up = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };

  const bump = (d) => onChange({ ...item, size: Math.max(ANNOT_MIN, Math.min(ANNOT_MAX, (item.size || ANNOT_DEFAULT) + d)) });
  const showTools = selected;
  // counter-scale the editing chrome so it stays usable when the form is zoomed down
  const inv = 1 / Math.max(0.25, uiScale || 1);
  const toolStyle = { transform: `scale(${inv})`, transformOrigin: 'left top' };

  // keyboard: Esc finishes the note; Delete/Backspace on an empty note removes it
  const onKeyDown = (e) => {
    if (e.key === 'Escape') { e.preventDefault(); onDone && onDone(); if (taRef.current) taRef.current.blur(); return; }
    if ((e.key === 'Delete' || e.key === 'Backspace') && !item.text) { e.preventDefault(); onRemove(); }
  };

  return (
    <div className={'annot-item' + (selected ? ' sel' : '')}
      style={{ left: item.x + 'px', top: item.y + 'px' }}
      onPointerDown={(e) => e.stopPropagation()}
      onMouseDown={(e) => e.stopPropagation()}>
      {showTools && (
        <div className="annot-tools" contentEditable={false} style={toolStyle}>
          <span className="an-grip" title="Drag to move" onPointerDown={onGripDown}>⠿</span>
          <button type="button" className="an-btn" title="Smaller" onMouseDown={(e)=>e.preventDefault()} onClick={() => bump(-1)}>A−</button>
          <button type="button" className="an-btn" title="Bigger"  onMouseDown={(e)=>e.preventDefault()} onClick={() => bump(1)}>A+</button>
          <button type="button" className="an-btn an-done" title="Done" onMouseDown={(e)=>e.preventDefault()} onClick={() => { if (taRef.current) taRef.current.blur(); onDone && onDone(); }}>✓</button>
          <button type="button" className="an-btn an-del" title="Delete" onMouseDown={(e)=>e.preventDefault()} onClick={onRemove}>✕</button>
        </div>
      )}
      <textarea
        ref={taRef}
        className="annot-text"
        value={item.text}
        wrap="off"
        spellCheck={false}
        rows={1}
        placeholder="type…"
        style={{ fontSize: (item.size || ANNOT_DEFAULT) + 'px', color: ink }}
        onFocus={onSelect}
        onKeyDown={onKeyDown}
        onChange={(e) => { onChange({ ...item, text: e.target.value }); }}
        onInput={autosize}
        onBlur={() => { if (!item.text.trim()) onRemove(); }}
      />
    </div>
  );
}

// The full layer. `active` = "Add text" mode (click empty space to place a note).
function AnnotationLayer({ formW, formH, ink, items, onChange, active, uiScale }) {
  const layerRef = useAR(null);
  const [selectedId, setSelectedId] = useAS(null);
  const [justAdded, setJustAdded] = useAS(null);

  const toFormCoords = useACB((e) => {
    const el = layerRef.current;
    if (!el) return { x: 0, y: 0 };
    const r = el.getBoundingClientRect();
    const scale = r.width / formW || 1;
    return { x: (e.clientX - r.left) / scale, y: (e.clientY - r.top) / scale };
  }, [formW]);

  useAE(() => { if (!active) setSelectedId(null); }, [active]);

  const list = items || [];
  const update = (it) => onChange(list.map(x => x.id === it.id ? it : x));
  const remove = (id) => { onChange(list.filter(x => x.id !== id)); setSelectedId(s => s === id ? null : s); };

  // Click EMPTY space on the layer (add mode) → new note. We listen on the layer
  // itself (not a cover div) and only act when the click hit the bare background
  // — clicks on an existing note stopPropagation, so they edit instead of stacking.
  const onLayerDown = (e) => {
    if (!active) return;
    if (e.target !== layerRef.current) return;   // hit a note/tool → ignore
    e.preventDefault();
    const p = toFormCoords(e);
    const size = ANNOT_DEFAULT;
    const it = { id: annotUid(), x: Math.round(p.x), y: Math.round(p.y - size * 0.6), size, text: '' };
    onChange([...list, it]);
    setSelectedId(it.id);
    setJustAdded(it.id);
  };

  return (
    <div ref={layerRef}
      className={'annot-layer' + (active ? ' on' : '')}
      style={{ width: formW + 'px', height: formH + 'px' }}
      onPointerDown={onLayerDown}>
      {active && list.length === 0 && (
        <div className="annot-prompt"><span>✎ Click anywhere on the form to type</span></div>
      )}
      {list.map(it => (
        <AnnotItem key={it.id} item={it} ink={ink}
          active={active}
          uiScale={uiScale}
          selected={selectedId === it.id}
          autoFocus={justAdded === it.id}
          onSelect={() => setSelectedId(it.id)}
          onDone={() => setSelectedId(null)}
          onChange={update}
          onRemove={() => remove(it.id)}
          toFormCoords={toFormCoords} />
      ))}
    </div>
  );
}

Object.assign(window, { AnnotationLayer, ANNOT_MIN, ANNOT_MAX, ANNOT_DEFAULT });
