// MarkdownSourceEditor.jsx — the Contribute-mode raw markdown source editor.
//
// CodeMirror 6 per §18 (Phase 1 of the Contribute rewrite). The editing
// surface is plain markdown; the rendered preview lives in a sibling pane
// (added in Phase 2). No HTML→markdown round-trip — `getDoc()` returns
// the source verbatim, which is what we POST in §8.11's manual flush.
//
// The parent receives an imperative handle on `editorRef`:
//   { view, getDoc(), setDoc(text) }
// modeled lightly after the Tiptap surface so RFCView can branch on
// whichever editor is mounted without an adapter layer.

import { useEffect, useRef } from 'react'
import { EditorState } from '@codemirror/state'
import { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter } from '@codemirror/view'
import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands'
import { markdown } from '@codemirror/lang-markdown'
import { syntaxHighlighting, defaultHighlightStyle, indentOnInput, bracketMatching } from '@codemirror/language'
import { diffGutterExtension, setBaseline } from './diffGutterExtension.js'

export default function MarkdownSourceEditor({
  initialDoc = '',
  editorRef,
  onUpdate,
}) {
  const hostRef = useRef(null)
  const viewRef = useRef(null)
  const onUpdateRef = useRef(onUpdate)

  useEffect(() => { onUpdateRef.current = onUpdate }, [onUpdate])

  useEffect(() => {
    if (!hostRef.current) return
    const state = EditorState.create({
      doc: initialDoc,
      extensions: [
        history(),
        diffGutterExtension(),
        lineNumbers(),
        highlightActiveLine(),
        highlightActiveLineGutter(),
        indentOnInput(),
        bracketMatching(),
        keymap.of([indentWithTab, ...defaultKeymap, ...historyKeymap]),
        markdown(),
        syntaxHighlighting(defaultHighlightStyle),
        EditorView.lineWrapping,
        EditorView.updateListener.of(u => {
          if (u.docChanged) {
            onUpdateRef.current?.(u.state.doc.toString())
          }
        }),
      ],
    })
    const view = new EditorView({ state, parent: hostRef.current })
    // Seed the §8.10 gutter baseline to the initial doc — nothing
    // diverges at construct time. Subsequent baseline resets ride the
    // initialDoc-watching effect below so they dispatch atomically
    // with the doc replacement.
    view.dispatch({ effects: setBaseline.of(initialDoc ?? '') })
    viewRef.current = view
    if (editorRef) {
      editorRef.current = {
        view,
        getDoc: () => view.state.doc.toString(),
        setDoc: (text) => {
          const cur = view.state.doc.toString()
          if (cur === text) return
          view.dispatch({
            changes: { from: 0, to: view.state.doc.length, insert: text },
          })
        },
      }
    }
    return () => {
      view.destroy()
      viewRef.current = null
      if (editorRef && editorRef.current?.view === view) editorRef.current = null
    }
    // Construct once. Doc changes flow through the setDoc effect below.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // Reflect external doc changes (branch reload, server-side flush, etc).
  // The §8.10 gutter baseline is reset in the same dispatch so the
  // newly-pushed doc doesn't briefly read as "divergent" against the
  // old baseline.
  useEffect(() => {
    const v = viewRef.current
    if (!v) return
    if (v.state.doc.toString() === initialDoc) return
    v.dispatch({
      changes: { from: 0, to: v.state.doc.length, insert: initialDoc ?? '' },
      effects: setBaseline.of(initialDoc ?? ''),
    })
  }, [initialDoc])

  return <div ref={hostRef} className="cm-source-editor" />
}
