/**
 * CodeEditorPane - Code editor panel with tabs and CodeMirror 6 integration
 *
 * Features:
 * - Syntax highlighting for HTML, CSS, and JavaScript
 * - Auto-completion and error checking
 * - Line numbers and code folding
 * - Theme support with proper styling
 */

import React, {
  forwardRef, useImperativeHandle, useRef, useEffect, useCallback,
} from 'react';
import PropTypes from 'prop-types';

// CodeMirror 6 imports
import { EditorState } from '@codemirror/state';
import {
  EditorView, lineNumbers, highlightActiveLine, placeholder,
} from '@codemirror/view';

// Define Theme and Highlighting inline to avoid "multiple instances of @codemirror/state" error


import { injectIntl, intlShape } from 'react-intl';

// Messages
import CapRow from '@capillarytech/cap-ui-library/CapRow';
import { createRobustExtensions } from '../../utils/properSyntaxHighlighting';
import messages from '../../messages';

// Cap UI Components

// Components
import TagList from '../../../../v2Containers/TagList';
import ValidationErrorDisplay from '../ValidationErrorDisplay';

// Constants - removed unused imports since tag fetching is handled by parent

// Context
import { useEditorContext } from '../common/EditorContext';

// Styles
import './_codeEditorPane.scss';
import { INAPP } from '../../../../constants/unified';

// Define Theme and Highlighting inline to avoid "multiple instances of @codemirror/state" error


// Legacy CodeMirrorEditor removed - using enhanced implementation only

const CodeEditorPaneComponent = ({
  intl,
  readOnly = false,
  className = '',
  forwardedRef,
  // Tag-related props - tags are fetched and managed by parent component
  tags = [],
  injectedTags = {},
  location,
  eventContextTags = [],
  waitEventContextTags = {},
  selectedOfferDetails = [],
  channel,
  userLocale = 'en',
  moduleFilterEnabled = true,
  onTagContextChange,
  // Validation props
  onErrorClick,
}) => {
  const context = useEditorContext();
  const {
    content, validation, variant,
  } = context || {};
  const { content: contentValue, updateContent } = content || {};
  const editorRef = useRef(null);
  const viewRef = useRef(null);

  // Create a ref to the latest updateContent function to avoid stale closures
  const updateContentRef = useRef(updateContent);
  updateContentRef.current = updateContent;

  // Expose methods and EditorView instance via ref
  useImperativeHandle(forwardedRef, () => ({
    // Expose the EditorView instance directly for parent access
    get view() {
      return viewRef.current;
    },
    viewRef, // For compatibility with existing code

    focus: () => {
      if (viewRef.current) {
        viewRef.current.focus();
      }
    },
    insertText: (text, position) => {
      if (viewRef.current) {
        const view = viewRef.current;
        const { state: { selection: { main: { head } } } } = view;
        const pos = position !== undefined ? position : head;
        view.dispatch({
          changes: { from: pos, insert: text },
          selection: { anchor: pos + text.length },
        });
      } else {
        throw new Error('CodeMirror view not initialized');
      }
    },
    getCursor: () => {
      if (viewRef.current) {
        const { state: { selection: { main: { head } } } } = viewRef.current;
        return head;
      }
      return 0;
    },
    getValue: () => contentValue || '',
    setValue: (value) => {
      updateContent(value);
    },
    navigateToLine: (line, column = 1) => {
      if (viewRef.current) {
        try {
          const view = viewRef.current;
          const { state: { doc } } = view;
          const { lines } = doc;
          const lineNumber = Math.min(Math.max(1, line), lines);
          const lineObj = doc.line(lineNumber);
          const { from, length } = lineObj;
          const pos = from + Math.min(Math.max(0, column - 1), length);

          view.dispatch({
            selection: { anchor: pos },
            effects: EditorView.scrollIntoView(pos, { y: 'center' }),
          });
          view.focus();
        } catch (error) {
          console.warn('Could not navigate to line:', error);
          if (viewRef.current) {
            viewRef.current.focus();
          }
        }
      }
    },
  }), [contentValue]);

  // Note: handleContentChange removed - using updateContentRef directly in CodeMirror

  const handleTagSelect = (tagData) => {
    // Get the unified editor
    if (viewRef.current) {
      const view = viewRef.current;
      const { state: { selection: { main: { head: pos } } } } = view;

      // Extract tag text from the tagData object using destructuring
      let tagText = '';
      if (typeof tagData === 'string') {
        tagText = tagData;
      } else if (tagData) {
        const {
          text, name, label, value,
        } = tagData;
        tagText = text || name || label || value;
        if (!tagText) {
          console.warn('Invalid tag data:', tagData);
          return;
        }
      } else {
        console.warn('Invalid tag data:', tagData);
        return;
      }

      // For unified HTML editor, insert as template variable
      const formattedTag = `{{${tagText}}}`;

      // Insert the tag at cursor position directly
      view.dispatch({
        changes: { from: pos, insert: formattedTag },
        selection: { anchor: pos + formattedTag.length },
      });

      // Focus back to editor
      view.focus();

      // Note: We don't call onLabelInsert here because:
      // 1. The tag is already inserted directly into the editor
      // 2. onLabelInsert (handleLabelInsert from HTMLEditor) would try to insert again
      // 3. This causes "Editor method not available" error
      // The direct insertion via view.dispatch is sufficient
    }
  };

  // Handle tag context change - delegate to parent component
  // Tags are fetched in parent components (EmailHTMLEditor, INAPP, etc.)
  // This component just passes the context change event up
  const handleTagContextChange = useCallback((data) => {
    if (onTagContextChange) {
      // Parent component handles tag fetching and updates
      onTagContextChange(data);
    }
    // No fallback - tags must be managed by parent component
  }, [onTagContextChange]);

  // Initialize CodeMirror effect
  useEffect(() => {
    // Ensure editorRef.current is a valid DOM element before initializing CodeMirror
    if (editorRef.current && editorRef.current instanceof HTMLElement && !viewRef.current) {
      // Determine placeholder text based on channel
      let placeholderText = intl.formatMessage(messages.editorPlaceholderEmail); // Default to email
      if (channel === INAPP || channel === INAPP.toUpperCase()) {
        placeholderText = intl.formatMessage(messages.editorPlaceholderInapp);
      }
      // Use the comprehensive extensions from properSyntaxHighlighting.js
      // This includes: html(), syntaxHighlighting(comprehensiveVSCodeTheme), cleanEditorTheme
      const robustExtensions = createRobustExtensions();

      // Add additional extensions for line numbers, active line, placeholder, line wrapping, and update listener
      const extensions = [
        lineNumbers(),
        highlightActiveLine(),
        placeholder(placeholderText),
        EditorView.lineWrapping, // Enable soft-wrapping of long lines
        ...robustExtensions, // Spread the robust extensions (html, syntax highlighting, theme)
        EditorView.updateListener.of((update) => {
          if (update.docChanged) {
            updateContentRef.current(update.state.doc.toString());
          }
        }),
      ];

      const state = EditorState.create({
        doc: contentValue || '',
        extensions,
      });

      viewRef.current = new EditorView({
        state,
        parent: editorRef.current,
      });
    }

    return () => {
      // Cleanup cursor styling
      if (editorRef.current && editorRef.current._cleanupCursorStyling) {
        editorRef.current._cleanupCursorStyling();
      }

      if (viewRef.current) {
        viewRef.current.destroy();
        viewRef.current = null;
      }
    };
  }, [channel, intl]);

  // Update editor content when content changes
  useEffect(() => {
    if (viewRef.current && contentValue && contentValue !== viewRef.current.state.doc.toString()) {
      const { current: view } = viewRef;
      const { state: { doc: { length } } } = view;
      view.dispatch({
        changes: {
          from: 0,
          to: length,
          insert: contentValue || '',
        },
      });
    }
  }, [contentValue]);

  return (
    <CapRow className={`code-editor-pane ${className}`}>
      {/* Code Editor Content - Stacked vertically with validation */}
      <div className="code-editor-pane__wrapper">
        {/* Code Editor with Floating Add Label Button */}
        <div className="codemirror-wrapper">
          <div ref={editorRef} className="codemirror-editor" />
          {/* Floating Add Label Button */}
          <CapRow className="code-editor-pane__actions">
            <TagList
              key="html-editor-taglist"
              label={intl.formatMessage(messages.addLabel)}
              onTagSelect={handleTagSelect}
              onContextChange={handleTagContextChange}
              className="tag-list-trigger"
              tags={tags}
              injectedTags={injectedTags}
              moduleFilterEnabled={moduleFilterEnabled}
              userLocale={userLocale}
              channel={channel}
              disabled={readOnly}
              location={location}
              selectedOfferDetails={selectedOfferDetails}
              eventContextTags={eventContextTags}
              waitEventContextTags={waitEventContextTags}
              popoverPlacement="rightTop"
            />
          </CapRow>
        </div>

        {/* Validation Error Display - Below editor, always visible */}
        <ValidationErrorDisplay
          validation={validation}
          onErrorClick={onErrorClick}
          className="code-editor-pane__validation"
        />
      </div>
    </CapRow>
  );
};

// Apply injectIntl to the component first, then wrap with forwardRef
const CodeEditorPaneWithIntl = injectIntl(CodeEditorPaneComponent);

// Create the forwardRef wrapper that forwards ref to the intl-wrapped component
const CodeEditorPane = forwardRef((props, ref) => (
  <CodeEditorPaneWithIntl {...props} forwardedRef={ref} />
));

CodeEditorPane.displayName = 'CodeEditorPane';

CodeEditorPane.propTypes = {
  intl: intlShape.isRequired,
  readOnly: PropTypes.bool,
  className: PropTypes.string,
  isFullscreenMode: PropTypes.bool,
  onLabelInsert: PropTypes.func,
  onErrorClick: PropTypes.func,
  // Tag-related props - tags are fetched and managed by parent component
  tags: PropTypes.array,
  injectedTags: PropTypes.object,
  location: PropTypes.object,
  eventContextTags: PropTypes.array,
  waitEventContextTags: PropTypes.object,
  selectedOfferDetails: PropTypes.array,
  channel: PropTypes.string,
  userLocale: PropTypes.string,
  moduleFilterEnabled: PropTypes.bool,
  onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
};

// Export the forwardRef-wrapped component
export default CodeEditorPane;
