import { useEffect, useRef } from 'react';
import { EditorState, type Extension } from '@codemirror/state';
import { EditorView, keymap, highlightSpecialChars, drawSelection, highlightActiveLine, lineNumbers } from '@codemirror/view';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
import { lintKeymap, lintGutter } from '@codemirror/lint';
import { mustacheCompletionSource } from './mustache-completion';
import { mustacheLinter } from './mustache-linter';
import { availableVariables } from './alert-variables';
import type { ConditionKind } from '../../pages/Alerts/enums';
import css from './MustacheEditor.module.css';

export interface MustacheEditorProps {
  value: string;
  onChange: (value: string) => void;
  kind?: ConditionKind;
  reducedContext?: boolean;  // connection URL editor uses env-only context
  label: string;
  placeholder?: string;
  minHeight?: number;         // default 80
  singleLine?: boolean;       // used for header values / URL fields
}

export function MustacheEditor(props: MustacheEditorProps) {
  const hostRef = useRef<HTMLDivElement>(null);
  const viewRef = useRef<EditorView | null>(null);

  // Keep a ref to the latest onChange so the EditorView effect doesn't re-create on every render.
  const onChangeRef = useRef(props.onChange);
  onChangeRef.current = props.onChange;

  useEffect(() => {
    if (!hostRef.current) return;
    const allowed = availableVariables(props.kind, { reducedContext: props.reducedContext });

    const extensions: Extension[] = [
      history(),
      drawSelection(),
      highlightSpecialChars(),
      highlightActiveLine(),
      closeBrackets(),
      autocompletion({ override: [mustacheCompletionSource(allowed)] }),
      mustacheLinter(allowed),
      lintGutter(),
      EditorView.updateListener.of((u) => {
        if (u.docChanged) onChangeRef.current(u.state.doc.toString());
      }),
      keymap.of([
        ...closeBracketsKeymap,
        ...defaultKeymap,
        ...historyKeymap,
        ...completionKeymap,
        ...lintKeymap,
      ]),
    ];
    if (!props.singleLine) extensions.push(lineNumbers());
    if (props.singleLine) {
      // Prevent Enter from inserting a newline on single-line fields.
      extensions.push(
        EditorState.transactionFilter.of((tr) => {
          if (tr.newDoc.lines > 1) return [];
          return tr;
        }),
      );
    }

    const view = new EditorView({
      parent: hostRef.current,
      state: EditorState.create({
        doc: props.value,
        extensions,
      }),
    });
    viewRef.current = view;
    return () => {
      view.destroy();
      viewRef.current = null;
    };
    // Extensions built once per mount; prop-driven extensions rebuilt in the effect below.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [props.kind, props.reducedContext, props.singleLine]);

  // If the parent replaces `value` externally (e.g. promotion prefill), sync the doc.
  useEffect(() => {
    const view = viewRef.current;
    if (!view) return;
    const current = view.state.doc.toString();
    if (current !== props.value) {
      view.dispatch({ changes: { from: 0, to: current.length, insert: props.value } });
    }
  }, [props.value]);

  const minH = props.minHeight ?? (props.singleLine ? 32 : 80);

  return (
    <div className={css.wrapper}>
      <label className={css.label}>{props.label}</label>
      <div
        ref={hostRef}
        className={css.editor}
        role="textbox"
        aria-label={props.label}
        style={{ minHeight: minH }}
        data-placeholder={props.placeholder}
      />
    </div>
  );
}
