// QuillEditor.jsx — thin React wrapper around Quill 2 (loaded from CDN
// in index.html). Used for rich-text body editing on the Training item
// form. Outputs HTML directly into the existing body_html column.
//
// Image button is overridden to upload the picked file to the
// training-media Supabase Storage bucket, then insert an <img> with
// the public URL into the editor. Video button uses Quill's stock
// behavior (iframe embed by URL — works with YouTube / Vimeo).

const QUILL_BUCKET = 'training-media';

const QUILL_TOOLBAR = [
  [{ header: [2, 3, false] }],
  ['bold', 'italic', 'underline', 'strike'],
  [{ list: 'ordered' }, { list: 'bullet' }],
  ['blockquote', 'code-block'],
  ['link', 'image', 'video'],
  ['clean'],
];

const QuillEditor = ({ value, onChange, placeholder, minHeight = 240, contextLabel }) => {
  const hostRef = useRef(null);
  const quillRef = useRef(null);
  // Only set the editor's HTML from the prop when it changes EXTERNALLY
  // (e.g., switching to a different training item). Setting it on
  // every keystroke would wipe the cursor every time.
  const lastValueRef = useRef(value);

  // AI rewrite state — `rewriteState` holds the preview modal payload.
  // null    : closed
  // 'loading' : Edge Function in flight (shows spinner in the toolbar)
  // { original, rewritten, mode, range } : preview ready
  const [rewriteState, setRewriteState] = useState(null);

  // Pulls the current selection text + range from Quill. If no
  // selection (or selection has 0 length), returns the full body HTML
  // so the user can rewrite the whole document with one click.
  const collectRewriteInput = () => {
    const q = quillRef.current;
    if (!q) return null;
    const range = q.getSelection();
    if (range && range.length > 0) {
      const text = q.getText(range.index, range.length);
      return { mode: 'selection', text, range };
    }
    return { mode: 'document', text: q.root.innerHTML || '', range: null };
  };

  const runRewrite = async () => {
    const payload = collectRewriteInput();
    if (!payload) return;
    if (!payload.text || !payload.text.trim()) {
      alert('Nothing to rewrite — type something or select a passage first.');
      return;
    }
    setRewriteState('loading');
    try {
      const { data, error } = await window.supa.functions.invoke('rewrite-text', {
        body: {
          text: payload.text,
          format: payload.mode === 'document' ? 'html' : 'plain',
          context: contextLabel || '',
        },
      });
      if (error) throw error;
      if (!data?.rewritten) throw new Error('Empty response from rewrite-text');
      setRewriteState({
        mode: payload.mode,
        range: payload.range,
        original: payload.text,
        rewritten: data.rewritten,
      });
    } catch (e) {
      console.error('rewrite failed', e);
      alert('AI rewrite failed — ' + (e?.message || 'unknown error'));
      setRewriteState(null);
    }
  };

  const applyRewrite = () => {
    const q = quillRef.current;
    if (!q || !rewriteState || rewriteState === 'loading') return;
    if (rewriteState.mode === 'selection' && rewriteState.range) {
      q.deleteText(rewriteState.range.index, rewriteState.range.length, 'user');
      q.insertText(rewriteState.range.index, rewriteState.rewritten, 'user');
      q.setSelection(rewriteState.range.index, rewriteState.rewritten.length, 'silent');
    } else {
      // Whole-document rewrite — replace contents via clipboard.convert
      // so HTML structure round-trips through Quill's blot model.
      const delta = q.clipboard.convert({ html: rewriteState.rewritten });
      q.setContents(delta, 'user');
    }
    setRewriteState(null);
  };

  useEffect(() => {
    if (!hostRef.current || quillRef.current) return;
    if (typeof window.Quill !== 'function') {
      console.warn('QuillEditor: window.Quill not loaded yet');
      return;
    }

    const q = new window.Quill(hostRef.current, {
      theme: 'snow',
      placeholder: placeholder || 'Write the training body…',
      modules: {
        toolbar: {
          container: QUILL_TOOLBAR,
          handlers: {
            // Custom image handler — pops a file picker, uploads to
            // Supabase Storage, inserts the URL. Avoids Quill's default
            // base64-embed behavior which bloats the HTML payload.
            image: async function () {
              const input = document.createElement('input');
              input.type = 'file';
              input.accept = 'image/*';
              input.onchange = async () => {
                const file = input.files?.[0];
                if (!file) return;
                try {
                  const url = await uploadInlineImage(file);
                  if (!url) return;
                  const range = q.getSelection(true);
                  q.insertEmbed(range.index, 'image', url, 'user');
                  q.setSelection(range.index + 1, 0, 'silent');
                } catch (e) {
                  console.error('QuillEditor image upload failed', e);
                  alert('Image upload failed — see console.');
                }
              };
              input.click();
            },
          },
        },
        clipboard: { matchVisual: false },
      },
    });

    if (value) {
      // pasteHTML preserves existing inline tags from the body_html
      // column on first mount.
      const delta = q.clipboard.convert({ html: value });
      q.setContents(delta, 'silent');
    }

    // Paste interceptor — if the clipboard carries an image file (or HTML
    // with an inline data: image), upload it to Supabase Storage first and
    // insert the resulting URL. Without this, Quill embeds the file as a
    // multi-megabyte base64 data URI inside body_html, which blows up the
    // saved row size and makes the AI rewrite/summarize Edge Function
    // reject the payload.
    hostRef.current.addEventListener('paste', async (e) => {
      const items = e.clipboardData?.items;
      if (!items) return;
      for (const item of items) {
        if (item.kind === 'file' && item.type.startsWith('image/')) {
          e.preventDefault();
          e.stopPropagation();
          const file = item.getAsFile();
          if (!file) return;
          try {
            const url = await uploadInlineImage(file);
            if (!url) return;
            const range = q.getSelection(true) || { index: q.getLength(), length: 0 };
            q.insertEmbed(range.index, 'image', url, 'user');
            q.setSelection(range.index + 1, 0, 'silent');
          } catch (err) {
            console.error('QuillEditor paste-image upload failed', err);
            alert('Image upload failed — see console.');
          }
          return;
        }
      }
    }, true);

    // Drop interceptor — same reasoning, for files dragged into the
    // editor.
    hostRef.current.addEventListener('drop', async (e) => {
      const files = e.dataTransfer?.files;
      if (!files || files.length === 0) return;
      const imageFiles = Array.from(files).filter(f => f.type.startsWith('image/'));
      if (imageFiles.length === 0) return;
      e.preventDefault();
      e.stopPropagation();
      for (const file of imageFiles) {
        try {
          const url = await uploadInlineImage(file);
          if (!url) continue;
          const range = q.getSelection(true) || { index: q.getLength(), length: 0 };
          q.insertEmbed(range.index, 'image', url, 'user');
          q.setSelection(range.index + 1, 0, 'silent');
        } catch (err) {
          console.error('QuillEditor drop-image upload failed', err);
          alert('Image upload failed — see console.');
        }
      }
    }, true);

    q.on('text-change', () => {
      const html = q.root.innerHTML;
      // Quill leaves a stray "<p><br></p>" when the editor is empty —
      // normalize to empty string so the DB doesn't carry junk markup.
      const normalized = html === '<p><br></p>' ? '' : html;
      lastValueRef.current = normalized;
      onChange && onChange(normalized);
    });

    quillRef.current = q;
  }, []);

  // External value swap (e.g. opening a different training item).
  useEffect(() => {
    const q = quillRef.current;
    if (!q) return;
    if (value === lastValueRef.current) return;
    const delta = q.clipboard.convert({ html: value || '' });
    q.setContents(delta, 'silent');
    lastValueRef.current = value || '';
  }, [value]);

  return (
    <div className="quill-host" style={{ minHeight, position: 'relative' }}>
      <div ref={hostRef} style={{ minHeight }} />

      {/* AI rewrite trigger — pinned to the top-right of the editor.
          The Quill toolbar runs out of room for a sensible custom
          button, and a floating chip reads more like a magic action
          than a formatting toggle. */}
      <button
        type="button"
        onClick={runRewrite}
        disabled={rewriteState === 'loading'}
        title="Rewrite the selected text, or the whole body if nothing is selected"
        style={{
          position: 'absolute',
          top: 6, right: 8,
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '5px 10px', borderRadius: 999,
          background: rewriteState === 'loading' ? 'var(--bg-sunken)' : '#FFFFFF',
          border: '1px solid var(--border-1)',
          color: '#7C3AED',
          fontSize: 11.5, fontWeight: 600,
          cursor: rewriteState === 'loading' ? 'wait' : 'pointer',
          opacity: rewriteState === 'loading' ? 0.65 : 1,
          boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
          zIndex: 2,
        }}
      >
        <PIcon name="sparkle" size={12} color="#7C3AED" />
        {rewriteState === 'loading' ? '…' : 'Rewrite'}
      </button>

      {rewriteState && rewriteState !== 'loading' && (
        <RewritePreviewModal
          state={rewriteState}
          onCancel={() => setRewriteState(null)}
          onReplace={applyRewrite}
        />
      )}
    </div>
  );
};

// Side-by-side preview so the admin sees exactly what's about to
// replace their text. HTML mode renders Original vs Rewritten as
// formatted previews; plain-text mode shows them as code-style blocks.
const RewritePreviewModal = ({ state, onCancel, onReplace }) => {
  const isHtml = state.mode === 'document';
  return (
    <PModal
      open
      onClose={onCancel}
      title={isHtml ? 'AI rewrite — whole body' : 'AI rewrite — selection'}
      width={820}
      footer={
        <>
          <PBtn variant="secondary" onClick={onCancel}>Cancel</PBtn>
          <PBtn variant="primary" onClick={onReplace}>Replace with rewrite</PBtn>
        </>
      }
    >
      <div style={{ padding: 16 }}>
        <div style={{ fontSize: 12, color: 'var(--fg-3)', marginBottom: 10 }}>
          Same meaning, polished English. Review and replace, or cancel to keep the original.
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <PreviewPane label="Original" tone="muted" html={isHtml} content={state.original} />
          <PreviewPane label="Rewritten" tone="accent" html={isHtml} content={state.rewritten} />
        </div>
      </div>
    </PModal>
  );
};

const PreviewPane = ({ label, tone, html, content }) => {
  const accent = tone === 'accent';
  return (
    <div style={{
      border: '1px solid ' + (accent ? '#C7B8F0' : 'var(--border-2)'),
      borderRadius: 10, overflow: 'hidden',
      background: accent ? '#FAF5FF' : '#FBFAF8',
    }}>
      <div style={{
        padding: '6px 12px',
        background: accent ? '#EDE9FE' : 'var(--bg-sunken)',
        fontSize: 10.5, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase',
        color: accent ? '#5B21B6' : 'var(--fg-3)',
        borderBottom: '1px solid ' + (accent ? '#DDD1F5' : 'var(--border-2)'),
      }}>{label}</div>
      <div style={{
        padding: 12,
        maxHeight: 380, overflow: 'auto',
        fontSize: 13, lineHeight: 1.55, color: 'var(--fg-1)',
        whiteSpace: html ? 'normal' : 'pre-wrap',
        wordBreak: 'break-word',
      }}>
        {html
          ? <div className="ql-snow"><div className="ql-editor" style={{ padding: 0 }} dangerouslySetInnerHTML={{ __html: content }} /></div>
          : content}
      </div>
    </div>
  );
};

async function uploadInlineImage(file) {
  if (!window.supa) throw new Error('Supabase client not initialized');
  const ext = (file.name.split('.').pop() || 'png')
    .toLowerCase()
    .replace(/[^a-z0-9]/g, '') || 'png';
  const uuid = crypto.randomUUID
    ? crypto.randomUUID()
    : (Date.now() + '-' + Math.random().toString(36).slice(2));
  const restId = window.RESTAURANT_ID || 'no-rest';
  const key = `${restId}/${uuid}.${ext}`;
  const { error } = await window.supa.storage
    .from(QUILL_BUCKET)
    .upload(key, file, {
      contentType: file.type || 'image/' + ext,
      upsert: false,
    });
  if (error) throw error;
  const { data } = window.supa.storage.from(QUILL_BUCKET).getPublicUrl(key);
  return data?.publicUrl || null;
}

window.QuillEditor = QuillEditor;
