// mode — the Live/Source toggle seam (EDIT-02). Both modes share ONE EditorState
// document; a Compartment holds either the live-preview extension set or the
// source-mode set, and toggling reconfigures that compartment with a dispatch that
// carries `effects` ONLY (never `changes`). Because the document transaction is
// never touched, `doc.toString()` is byte-identical across Live⇄Source⇄Live by
// construction — that is the EDIT-02 guarantee mode.test.ts pins over the corpus.
//
// This plan keeps `liveExtensions` minimal: "Live" mode is functionally raw text
// plus the markdown language + theme seam. The actual inline-render ViewPlugin and
// image/table widgets land in 06-02/06-03, which extend `liveExtensions` here.
import { Compartment } from "@codemirror/state";
import { EditorView, keymap } from "@codemirror/view";
import type { Extension } from "@codemirror/state";
import { syntaxHighlighting } from "@codemirror/language";

import { markdownExtension } from "./markdown";
import { livePreviewExtension } from "./livePreview";
import { theme, sourceTheme } from "./theme";
import { codeHighlightStyle } from "./highlight";
import { useEditorMode } from "../../stores/editorMode";

// modeCompartment holds the mode-specific extension set; reconfiguring it is the
// whole toggle (no document edit).
export const modeCompartment = new Compartment();

// liveExtensions: the Live-preview configuration. 06-02 adds the livePreview
// ViewPlugin (inline text-construct decorations + active-line marker reveal);
// 06-03 appends the image/table widgets. The markdown language parses the tree
// the plugin walks; the theme renders the decoration classes at token values.
export const liveExtensions: Extension[] = [
  markdownExtension,
  livePreviewExtension,
  theme,
  // ED-04a: color nested fenced-code tokens via the ONE shared HighlightStyle. Because
  // codeHighlightStyle is scoped to CODE tags only (highlight.ts), this colors only the
  // nested ```lang token spans — the existing .cm-heading/.cm-strong/.cm-em decorations
  // (theme.ts) are untouched. Live only; Source mode stays raw mono (UI-SPEC).
  syntaxHighlighting(codeHighlightStyle),
];

// sourceExtensions: raw Markdown, monospace document-wide, no live-preview
// decorations. The markdown language stays loaded (syntax-aware editing) but no
// reveal/hide decorations are applied.
export const sourceExtensions: Extension[] = [markdownExtension, theme, sourceTheme];

// CmMode is the CM6 surface's OWN mode — strictly the two live-preview modes CM6
// can render. It is deliberately NARROWER than the store's EditorMode: "tiptap"
// is NOT a CM6 view (a TipTap surface isn't a CodeMirror view), so it can never
// reach the compartment config. The store carries "tiptap"; the CM6 keymap/setMode
// stay live⇄source only (MED-05).
export type CmMode = "live" | "source";

// setMode flips the compartment WITHOUT touching the document. The dispatch carries
// `effects` only — this is the byte-identity guarantee (EDIT-02). Never add
// `changes` here. The `mode` param is the narrow CmMode, so "tiptap" cannot be
// passed (a compile-time guard that the TipTap value never reconfigures a CM6 view).
export function setMode(view: EditorView, mode: CmMode): void {
  view.dispatch({
    effects: modeCompartment.reconfigure(
      mode === "live" ? liveExtensions : sourceExtensions,
    ),
  });
}

// toggleKeymap binds Cmd/Ctrl-E (CM6 `Mod-e` maps Cmd on macOS, Ctrl elsewhere) to
// flip the persisted editor-mode store and reconfigure the compartment to match.
// Reading the store after toggle() (rather than from a stale closure) keeps the
// keymap and the React toolbar control on the same single source of truth.
export const toggleKeymap = keymap.of([
  {
    key: "Mod-e",
    run: (view) => {
      useEditorMode.getState().toggle();
      // toggle() guarantees the resulting mode is live⇄source only (a "tiptap"
      // value flips back to "live"), so this narrows the 3-way store value to the
      // CM6 CmMode: "tiptap" can never reach setMode via the keymap (MED-05).
      const next = useEditorMode.getState().mode;
      setMode(view, next === "source" ? "source" : "live");
      return true;
    },
  },
]);
