/* global React, firebase */
/* ============================================
   EditableText / EditableImage / EditableLink
   - Wraps any text or image on the site.
   - Admin can toggle "Page Edit Mode" via a floating button.
   - In edit mode, text becomes contentEditable; images can be replaced.
   - Stored in Firestore collection: site_content/{id} { text | url | href }
   ============================================ */

(function () {
  // In-memory cache + subscribers (so all components stay in sync)
  const CACHE = {};      // { id: { text, url, href } }
  const LISTENERS = {};  // { id: [fn, fn, ...] }
  let initialized = false;

  function notify(id) {
    (LISTENERS[id] || []).forEach(fn => { try { fn(CACHE[id]); } catch (e) {} });
  }

  function initFirestoreSync() {
    if (initialized || !window.fb) return;
    initialized = true;
    window.fb.db.collection('site_content').onSnapshot(snap => {
      snap.docChanges().forEach(ch => {
        const id = ch.doc.id;
        if (ch.type === 'removed') delete CACHE[id];
        else CACHE[id] = ch.doc.data() || {};
        notify(id);
      });
    }, err => console.warn('site_content sync error:', err));
  }
  if (window.fb) initFirestoreSync();
  else setTimeout(initFirestoreSync, 500);

  function useContent(id) {
    const [val, setVal] = React.useState(CACHE[id] || null);
    React.useEffect(() => {
      if (!id) return;
      const fn = (v) => setVal(v || null);
      LISTENERS[id] = LISTENERS[id] || [];
      LISTENERS[id].push(fn);
      setVal(CACHE[id] || null);
      return () => {
        LISTENERS[id] = (LISTENERS[id] || []).filter(f => f !== fn);
      };
    }, [id]);
    return val;
  }

  function useEditMode() {
    const [on, setOn] = React.useState(!!window.__effEditMode);
    React.useEffect(() => {
      const h = () => setOn(!!window.__effEditMode);
      window.addEventListener('eff_edit_mode_changed', h);
      return () => window.removeEventListener('eff_edit_mode_changed', h);
    }, []);
    return on;
  }

  async function saveContent(id, patch) {
    if (!window.fb) return;
    await window.fb.db.collection('site_content').doc(id).set({
      ...patch,
      updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
    }, { merge: true });
  }

  /* ============================================
     <EditableText id tag="p" placeholder>...</EditableText>
     ============================================ */
  function childrenToText(children) {
    if (children == null || children === false) return '';
    if (typeof children === 'string' || typeof children === 'number') return String(children);
    if (Array.isArray(children)) return children.map(childrenToText).join('');
    if (React.isValidElement(children)) return childrenToText(children.props.children);
    return '';
  }

  function EditableText({ id, children, tag: Tag = 'span', className = '', style, placeholder = 'Click to edit' }) {
    const content = useContent(id);
    const editMode = useEditMode();
    const isAdmin = !!window.__effAdminMode;
    const ref = React.useRef(null);

    const stored = content?.text;
    const canEdit = !!(isAdmin && editMode);

    // Render strategy: render empty Tag, set textContent imperatively in a layout effect.
    // This prevents React from clobbering user edits during re-renders.
    React.useLayoutEffect(() => {
      const node = ref.current;
      if (!node) return;
      if (document.activeElement === node) return; // user is typing
      const fallback = childrenToText(children);
      const target = stored != null ? stored : fallback;
      const display = target || (canEdit ? placeholder : '');
      if (node.textContent !== display) node.textContent = display;
    }, [stored, canEdit, children, placeholder]);

    async function onBlur(e) {
      const next = (e.target.innerText || '').replace(/\u00A0/g, ' ').trimEnd();
      const fallback = childrenToText(children);
      const current = stored != null ? stored : fallback;
      if (next === current) return;
      try { await saveContent(id, { text: next }); }
      catch (err) { console.warn('Save EditableText failed:', err); }
    }

    function onKeyDown(e) {
      if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        e.target.blur();
      }
    }

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

    if (!id) return <Tag className={className} style={style}>{children}</Tag>;

    if (typeof Tag === 'string') {
      return (
        <Tag
          ref={ref}
          data-edit-id={id}
          className={`${className} ${canEdit ? 'eff-editable-on' : ''}`.trim()}
          contentEditable={canEdit}
          suppressContentEditableWarning
          spellCheck={false}
          onBlur={canEdit ? onBlur : undefined}
          onKeyDown={canEdit ? onKeyDown : undefined}
          onPaste={canEdit ? onPaste : undefined}
          style={style}
        />
      );
    }
    return <Tag className={className} style={style}>{stored != null ? stored : children}</Tag>;
  }

  /* ============================================
     <EditableImage id defaultSrc style alt>
     - Admin can click to upload a replacement
     - Uses Cloudinary
     ============================================ */
  function EditableImage({ id, defaultSrc, alt = '', style, className = '' }) {
    const content = useContent(id);
    const editMode = useEditMode();
    const isAdmin = !!window.__effAdminMode;
    const fileRef = React.useRef(null);
    const [uploading, setUploading] = React.useState(false);

    const url = content?.url || defaultSrc;
    const canEdit = !!(isAdmin && editMode);

    async function pick(file) {
      if (!file || !window.Cloudinary) return;
      setUploading(true);
      try {
        const u = await window.Cloudinary.uploadToCloudinary(file, `site/${id}`);
        await saveContent(id, { url: u });
      } catch (e) { alert('Upload failed: ' + e.message); }
      finally { setUploading(false); }
    }

    return (
      <span
        data-edit-id={id}
        className={`${className} ${canEdit ? 'eff-editable-on' : ''}`.trim()}
        style={{ position: 'relative', display: 'inline-block', ...style }}
      >
        {url ? <img src={url} alt={alt} style={{ display: 'block', maxWidth: '100%', height: 'auto' }} /> :
          (canEdit ? <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-3)' }}>[ no image ]</span> : null)}
        {canEdit && (
          <>
            <input ref={fileRef} type="file" accept="image/*" style={{ display: 'none' }}
              onChange={(e) => e.target.files[0] && pick(e.target.files[0])} />
            <button
              type="button"
              onClick={() => fileRef.current?.click()}
              style={{
                position: 'absolute', top: 6, right: 6, zIndex: 5,
                padding: '4px 8px', fontSize: 10, fontFamily: 'var(--font-mono)',
                background: 'rgba(5,6,13,0.92)', backdropFilter: 'blur(8px)',
                border: '1px solid var(--magenta)', borderRadius: 4,
                color: 'var(--magenta)', cursor: 'pointer', letterSpacing: '0.1em',
              }}>{uploading ? '...' : '↻ REPLACE'}</button>
          </>
        )}
      </span>
    );
  }

  /* ============================================
     Floating page-edit toggle (only when admin mode is on)
     ============================================ */
  function PageEditToggle() {
    const [adminOn, setAdminOn] = React.useState(!!window.__effAdminMode);
    const [editOn, setEditOn] = React.useState(!!window.__effEditMode);

    React.useEffect(() => {
      const h = () => setAdminOn(!!window.__effAdminMode);
      window.addEventListener('eff_admin_changed', h);
      return () => window.removeEventListener('eff_admin_changed', h);
    }, []);

    function toggle() {
      const next = !editOn;
      window.__effEditMode = next;
      document.body.classList.toggle('eff-edit-mode', next);
      window.dispatchEvent(new CustomEvent('eff_edit_mode_changed'));
      setEditOn(next);
    }

    if (!adminOn) return null;

    return (
      <button
        type="button"
        onClick={toggle}
        title={editOn ? 'Exit edit mode' : 'Enter edit mode (click any text to edit)'}
        style={{
          position: 'fixed', bottom: 100, right: 24, zIndex: 95,
          width: 56, height: 56, borderRadius: '50%',
          background: editOn ? 'linear-gradient(135deg, #FF2DAA, #FF2DAAcc)' : 'rgba(5,6,13,0.92)',
          color: editOn ? '#000' : 'var(--magenta)',
          border: `1px solid ${editOn ? '#fff3' : 'var(--magenta)'}`,
          cursor: 'pointer',
          display: 'grid', placeItems: 'center',
          boxShadow: editOn
            ? '0 0 24px rgba(255,45,170,0.9), 0 8px 24px rgba(0,0,0,0.5)'
            : '0 0 16px rgba(255,45,170,0.4), 0 6px 16px rgba(0,0,0,0.4)',
          backdropFilter: 'blur(8px)',
          transition: 'all 0.2s',
        }}
      >
        {editOn ? (
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <path d="M5 13l4 4L19 7" />
          </svg>
        ) : (
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 20h9" />
            <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z" />
          </svg>
        )}
        <span style={{
          position: 'absolute', right: 68, top: '50%', transform: 'translateY(-50%)',
          background: 'rgba(5,6,13,0.95)', backdropFilter: 'blur(8px)',
          border: '1px solid rgba(255,45,170,0.5)', borderRadius: 6,
          padding: '6px 10px', fontSize: 11, fontFamily: 'var(--font-mono)',
          color: 'var(--magenta)', whiteSpace: 'nowrap', letterSpacing: '0.05em',
          pointerEvents: 'none', opacity: 0,
        }} className="eff-edit-tooltip">
          {editOn ? 'EDITING · TAP TEXT' : 'EDIT THIS PAGE'}
        </span>
      </button>
    );
  }

  // Public API
  window.EditableText = EditableText;
  window.EditableImage = EditableImage;
  window.PageEditToggle = PageEditToggle;
  window.EditableSaveContent = saveContent;
})();
