import React, { useEffect, useRef } from 'react';

import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { json } from '@codemirror/lang-json';
import {
  bracketMatching,
  foldGutter,
  indentOnInput,
  syntaxHighlighting,
} from '@codemirror/language';
import { lintGutter } from '@codemirror/lint';
import { EditorState } from '@codemirror/state';
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
import {
  EditorView,
  highlightActiveLine,
  highlightActiveLineGutter,
  keymap,
  lineNumbers,
} from '@codemirror/view';
import { baseEditorTheme, jsonLinter } from '@renderer/utils/codemirrorTheme';

interface MembersJsonEditorProps {
  value: string;
  onChange: (json: string) => void;
  error: string | null;
}

const membersEditorTheme = EditorView.theme({
  '&': {
    fontSize: '12px',
    maxHeight: '300px',
  },
  '.cm-scroller': {
    overflow: 'auto',
  },
});

export const MembersJsonEditor = ({
  value,
  onChange,
  error,
}: MembersJsonEditorProps): React.JSX.Element => {
  const containerRef = useRef<HTMLDivElement>(null);
  const viewRef = useRef<EditorView | null>(null);
  const onChangeRef = useRef(onChange);
  onChangeRef.current = onChange;

  useEffect(() => {
    if (!containerRef.current) return;

    const state = EditorState.create({
      doc: value,
      extensions: [
        json(),
        lineNumbers(),
        highlightActiveLineGutter(),
        highlightActiveLine(),
        history(),
        foldGutter(),
        indentOnInput(),
        bracketMatching(),
        closeBrackets(),
        syntaxHighlighting(oneDarkHighlightStyle),
        jsonLinter,
        lintGutter(),
        keymap.of([...defaultKeymap, ...historyKeymap, ...closeBracketsKeymap]),
        baseEditorTheme,
        membersEditorTheme,
        EditorView.updateListener.of((update) => {
          if (update.docChanged) {
            onChangeRef.current(update.state.doc.toString());
          }
        }),
      ],
    });

    const view = new EditorView({
      state,
      parent: containerRef.current,
    });

    viewRef.current = view;

    return () => {
      view.destroy();
      viewRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps -- EditorView created once on mount
  }, []);

  useEffect(() => {
    const view = viewRef.current;
    if (!view) return;

    const currentDoc = view.state.doc.toString();
    if (currentDoc !== value) {
      view.dispatch({
        changes: { from: 0, to: currentDoc.length, insert: value },
      });
    }
  }, [value]);

  return (
    <div className="space-y-1">
      <div
        ref={containerRef}
        className="overflow-hidden rounded border border-[var(--color-border)]"
      />
      {error ? <p className="text-[11px] text-red-300">{error}</p> : null}
    </div>
  );
};
