// admin-reminders-rich-editor.jsx — Calendly-style rich editor for the
// reminder email body + subject.
//
// Behaviour:
// - User types plain text. The HTML it produces is what gets stored.
// - When the user clicks a placeholder chip, a styled pill is inserted
//   at the cursor position. The pill is contenteditable=false and
//   user-select=all so it behaves like a single token (one Backspace
//   deletes the whole thing).
// - Pills wrap a literal "{token}" text node. On save we strip the
//   pill spans so storage stays clean — backend's existing
//   `replace('{first_name}', value)` substitution keeps working.
// - On load, any `{token}` in the stored HTML is wrapped in a pill
//   for display.
//
// Public API:
//   <window.ARRichEditor value={html} onChange={(html) => ...}
//                        placeholder="..." singleLine={false}
//                        editorRef={ref} onFocus={...} />

(function () {
  const TOKEN_RE = /(\{[a-z_]+\})/g;

  // Wrap any {token} substring in HTML with a pill span.
  const htmlToEditor = (raw) => {
    if (!raw) return '';
    // We have to be careful not to wrap a token that is already inside
    // a span tag. The current storage has no spans, but be safe: only
    // wrap tokens that aren't preceded by `class="ar-pill"...>`.
    return raw.replace(TOKEN_RE, '<span class="ar-pill" contenteditable="false">$1</span>');
  };

  // Strip pills back to their inner {token} text for storage.
  const editorToHtml = (root) => {
    if (!root) return '';
    const clone = root.cloneNode(true);
    clone.querySelectorAll('.ar-pill').forEach(span => {
      span.replaceWith(document.createTextNode(span.innerText));
    });
    // Also strip the zero-width spaces we insert after each pill.
    let html = clone.innerHTML.replace(/​/g, '');
    return html;
  };

  const ARRichEditor = ({ value, onChange, placeholder, singleLine, editorRef, onFocus }) => {
    const innerRef = React.useRef(null);
    const lastWritten = React.useRef(null);

    React.useEffect(() => { window.arInstallPillStyles && window.arInstallPillStyles(); }, []);

    // editorRef can be a callback OR a ref object. We adapt both, and
    // ALSO keep our own innerRef so we don't depend on the parent's shape.
    const setRef = React.useCallback((el) => {
      innerRef.current = el;
      if (typeof editorRef === 'function') editorRef(el);
      else if (editorRef && typeof editorRef === 'object') editorRef.current = el;
    }, [editorRef]);

    // Initial / external value changes — only write to DOM when the
    // incoming value diverges from what we last emitted via onChange.
    React.useEffect(() => {
      if (!innerRef.current) return;
      const wrapped = htmlToEditor(value || '');
      if (lastWritten.current !== wrapped) {
        innerRef.current.innerHTML = wrapped;
        lastWritten.current = wrapped;
      }
    }, [value]);

    const handleInput = () => {
      const html = editorToHtml(innerRef.current);
      lastWritten.current = htmlToEditor(html);
      if (onChange) onChange(html);
    };

    const handlePaste = (e) => {
      e.preventDefault();
      const text = (e.clipboardData || window.clipboardData).getData('text/plain');
      document.execCommand('insertText', false, text);
    };

    const handleKeyDown = (e) => {
      if (singleLine && e.key === 'Enter') e.preventDefault();
    };

    return (
      <div
        ref={setRef}
        className={'ar-rich' + (singleLine ? ' ar-line' : '')}
        contentEditable={true}
        suppressContentEditableWarning={true}
        onInput={handleInput}
        onPaste={handlePaste}
        onKeyDown={handleKeyDown}
        onFocus={onFocus}
        data-placeholder={placeholder || ''}
        spellCheck={true}
      />
    );
  };

  window.ARRichEditor = ARRichEditor;
})();
