import React, { useEffect, useRef } from 'react';
import * as monaco from 'monaco-editor';

const registerVariableSupport = (baseLanguage = 'plaintext') => {
  const languageId = `${baseLanguage}-with-variables`;

  // Only register if not already registered
  if (!monaco.languages.getLanguages().some(lang => lang.id === languageId)) {
    // Register the new language
    monaco.languages.register({ id: languageId });

    // Define basic language configuration
    const configuration = {
      brackets: [
        ['{', '}'],
        ['[', ']'],
        ['(', ')'],
        ['{{', '}}']
      ],
      autoClosingPairs: [
        { open: '{', close: '}' },
        { open: '[', close: ']' },
        { open: '(', close: ')' },
        { open: '"', close: '"' },
        { open: '\'', close: '\'' },
        { open: '{{', close: '}}' }
      ],
      surroundingPairs: [
        { open: '{', close: '}' },
        { open: '[', close: ']' },
        { open: '(', close: ')' },
        { open: '"', close: '"' },
        { open: '\'', close: '\'' },
        { open: '{{', close: '}}' }
      ]
    };

    // Set the configuration for our new language
    monaco.languages.setLanguageConfiguration(languageId, configuration);

    // Define the token provider that combines base language and variable highlighting
    monaco.languages.setMonarchTokensProvider(languageId, {
      defaultToken: '',
      tokenizer: {
        root: [
          // Handle variables with highest priority
          [/{{[^}]*}}/, 'variable'],

          // Default token handling
          [/[^{{]+/, { token: '', switchTo: '@baseLanguage' }]
        ],

        baseLanguage: [
          [/{{/, { token: 'variable', switchTo: 'root' }],
          [/[^{{]+/, '']
        ]
      }
    });

    // Always define the theme - Monaco will handle duplicate definitions
    monaco.editor.defineTheme('variable-highlight-theme', {
      base: 'vs',
      inherit: true,
      rules: [
        { token: 'variable', foreground: '0000ff', fontStyle: 'bold' }
      ],
      colors: {}
    });
  }

  return languageId;
};

// Supported language configurations
const SUPPORTED_LANGUAGES = {
  json: {
    formatOnType: true,
    formatOnPaste: true,
    defaultValue: '{}',
    diagnosticsEnabled: true
  },
  javascript: {
    formatOnType: true,
    formatOnPaste: true,
    defaultValue: '',
    diagnosticsEnabled: true
  },
  html: {
    formatOnType: true,
    formatOnPaste: true,
    defaultValue: '',
    diagnosticsEnabled: true
  },
  xml: {
    formatOnType: true,
    formatOnPaste: true,
    defaultValue: '<?xml version="1.0" encoding="UTF-8"?>',
    diagnosticsEnabled: true
  },
  plaintext: {
    formatOnType: false,
    formatOnPaste: false,
    defaultValue: '',
    diagnosticsEnabled: false
  }
};

const MonacoVariableEditor = ({
  id,
  value = '',
  language = 'plaintext',
  readOnly = false,
  height = '64',
  onChange = () => {},
  options = {}
}) => {
  const editorRef = useRef(null);
  const containerRef = useRef(null);

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

    // Register language with variable support
    const editorLanguage = registerVariableSupport(language);

    // Get language-specific configurations
    const languageConfig = SUPPORTED_LANGUAGES[language] || SUPPORTED_LANGUAGES.plaintext;

    // Create editor instance
    editorRef.current = monaco.editor.create(containerRef.current, {
      value: value || languageConfig.defaultValue,
      language: editorLanguage,
      automaticLayout: true,
      theme: 'variable-highlight-theme',
      minimap: { enabled: false },
      readOnly,
      fontSize: 14,
      lineHeight: 21,
      padding: { top: 8, bottom: 8 },
      scrollBeyondLastLine: false,
      formatOnType: languageConfig.formatOnType,
      formatOnPaste: languageConfig.formatOnPaste,
      ...options,
    });

    // Add command to insert variable wrapper
    editorRef.current.addCommand(
        monaco.KeyMod.CtrlCmd | monaco.KeyCode.Space,
        () => {
          const selection = editorRef.current.getSelection();
          const selectedText = editorRef.current.getModel().getValueInRange(selection);

          const operation = {
            range: selection,
            text: selectedText ? `{{${selectedText}}}` : '{{}}'
          };

          editorRef.current.executeEdits('insert-variable', [operation]);

          // If no text was selected, place cursor inside brackets
          if (!selectedText) {
            const position = new monaco.Position(
                selection.startLineNumber,
                selection.startColumn + 2
            );
            editorRef.current.setPosition(position);
          }
        }
    );

    // Set up change event handler
    const changeDisposable = editorRef.current.onDidChangeModelContent(() => {
      const newValue = editorRef.current.getValue();
      onChange(newValue);
    });

    // Set up resize observer
    const resizeObserver = new ResizeObserver(() => {
      if (editorRef.current) {
        editorRef.current.layout();
      }
    });

    resizeObserver.observe(containerRef.current);

    // Cleanup
    return () => {
      changeDisposable.dispose();
      resizeObserver.disconnect();
      if (editorRef.current) {
        editorRef.current.dispose();
      }
    };
  }, [language]); // Re-initialize when language changes

  // Update value if it changes externally
  useEffect(() => {
    if (editorRef.current && value !== editorRef.current.getValue()) {
      editorRef.current.setValue(value);
    }
  }, [value]);

  return (
      <div
          id={id}
          ref={containerRef}
          className={`h-${height} border rounded-md overflow-hidden`}
          style={{ minHeight: '100px' }}
      />
  );
};

export default MonacoVariableEditor;
