/**
 * HTMLEditor - Comprehensive HTML Editor with Split-Screen Layout
 *
 * Features:
 * - Split-screen layout with code editor and live preview
 * - CodeMirror 6 integration for HTML/CSS/JavaScript editing
 * - Real-time preview with sandboxed iframe
 * - Mobile/Desktop responsive preview modes
 * - Comprehensive validation and error handling
 * - Security-first approach with XSS protection
 *
 * Note: Uses injectIntl with forwardRef to provide direct access to CodeEditorPane via ref
 */

import React, {
  useRef, useCallback, useMemo, useState, useEffect, useImperativeHandle, forwardRef,
} from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';

// Cap UI Components (First Preference)
import CapRow from '@capillarytech/cap-ui-library/CapRow';
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
import CapModal from '@capillarytech/cap-ui-library/CapModal';

// Component imports
import EditorToolbar from './components/EditorToolbar';
import DeviceToggle from './components/DeviceToggle';
import SplitContainer from './components/SplitContainer';
import CodeEditorPane from './components/CodeEditorPane';
import PreviewPane from './components/PreviewPane';
import { EditorProvider } from './components/common/EditorContext';

// Hooks and utilities
import { useEditorContent } from './hooks/useEditorContent';
import { useInAppContent } from './hooks/useInAppContent';
import { useLayoutState } from './hooks/useLayoutState';
import { useValidation } from './hooks/useValidation';

// Constants
import {
  HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT, TAG, EMBEDDED, DEFAULT, FULL, ALL, SMS, EMAIL,
  BLOCKING_ERROR_RULE_IDS,
  VALIDATION_SEVERITY,
} from './constants';

// Styles
import './_htmlEditor.scss';
import './components/FullscreenModal/_fullscreenModal.scss';

// Messages
import messages from './messages';
import { ISSUE_SOURCES } from './utils/validationConstants';

/** Check if an issue is a blocking error (Errors tab). Non-blocking = Warnings. */
const isBlockingError = (issue) => {
  const { rule, source, severity } = issue || {};
  if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') return true;
  if (source === ISSUE_SOURCES.LIQUID && severity === VALIDATION_SEVERITY.ERROR) return true;
  if (BLOCKING_ERROR_RULE_IDS.includes(rule)) return true;
  return false;
};

/** Count issues as Errors (blocking) and Warnings (non-blocking). */
const countIssuesBySeverity = (allIssues) => {
  let errors = 0;
  let warnings = 0;
  (allIssues || []).forEach((issue) => {
    if (isBlockingError(issue)) errors += 1;
    else warnings += 1;
  });
  return {
    errors,
    warnings,
    total: (allIssues || []).length,
  };
};

const HTMLEditor = forwardRef(({
  intl,
  variant = HTML_EDITOR_VARIANTS.EMAIL, // New prop: 'email' or 'inapp'
  layoutType, // Layout type for InApp variant
  initialContent = DEFAULT_HTML_CONTENT,
  onSave,
  onContentChange,
  className = '',
  readOnly = false,
  showFullscreenButton = true,
  autoSave = true,
  autoSaveInterval = 30000, // 30 seconds
  // Tag-related props - tags are fetched and managed by parent component (EmailHTMLEditor, INAPP, etc.)
  tags = [],
  injectedTags = {},
  location,
  eventContextTags = [],
  waitEventContextTags,
  selectedOfferDetails = [],
  channel,
  userLocale = 'en',
  moduleFilterEnabled = true,
  onTagContextChange, // Parent component handles tag fetching
  onTagSelect = null,
  onContextChange = null,
  globalActions = null,
  isFullMode = true, // Full mode vs library mode - controls layout and visibility
  onErrorAcknowledged = null, // Callback when user clicks redirection icon to acknowledge errors
  onValidationChange = null, // Callback when validation state changes (for parent to track errors)
  apiValidationErrors = null, // API validation errors from validateLiquidTemplateContent { liquidErrors: [], standardErrors: [] }
  ...props
}, ref) => {
  // Separate refs for main and modal editors to avoid conflicts
  const mainEditorRef = useRef(null);
  const modalEditorRef = useRef(null);
  const [isFullscreenModalOpen, setIsFullscreenModalOpen] = useState(false);

  // Get the currently active editor ref based on fullscreen state
  const getActiveEditorRef = useCallback(() => isFullscreenModalOpen ? modalEditorRef : mainEditorRef, [isFullscreenModalOpen]);

  // Initialize custom hooks for state management - always call both hooks to follow Rules of Hooks
  const isEmailVariant = variant === HTML_EDITOR_VARIANTS.EMAIL;

  const emailOptions = {
    autoSave: isEmailVariant ? autoSave : false,
    autoSaveInterval,
    onSave: isEmailVariant ? onSave : null,
    onChange: isEmailVariant ? onContentChange : null,
  };

  const emailContent = useEditorContent(
    isEmailVariant ? initialContent : '',
    emailOptions
  );

  // Prepare content for InApp variant
  const isInAppVariant = variant === HTML_EDITOR_VARIANTS.INAPP;

  let inAppInitialContent = {};
  if (isInAppVariant) {
    if (typeof initialContent === 'string') {
      // Convert string content to device-specific format
      inAppInitialContent = {
        [DEVICE_TYPES.ANDROID]: initialContent,
        [DEVICE_TYPES.IOS]: initialContent,
      };
    } else {
      // Use provided device-specific content
      inAppInitialContent = initialContent;
    }
  }

  const inAppOptions = {
    autoSave: isInAppVariant ? autoSave : false,
    autoSaveInterval,
    onSave: isInAppVariant ? onSave : null,
    onChange: isInAppVariant ? onContentChange : null,
  };

  const inAppContent = useInAppContent(inAppInitialContent, inAppOptions);

  // Use appropriate content hook based on variant
  const content = variant === HTML_EDITOR_VARIANTS.EMAIL ? emailContent : inAppContent;

  // Update content when initialContent prop changes (for edit mode)
  // This ensures the editor updates when template data loads
  useEffect(() => {
    if (isEmailVariant && emailContent && initialContent !== undefined && initialContent !== null) {
      // Only update if content is different to avoid unnecessary updates
      if (emailContent.content !== initialContent) {
        emailContent.updateContent(initialContent, true); // immediate update
      }
    } else if (isInAppVariant && inAppContent && initialContent !== undefined && initialContent !== null) {
      // Handle InApp variant updates
      const contentToUpdate = typeof initialContent === 'string'
        ? { [DEVICE_TYPES.ANDROID]: initialContent, [DEVICE_TYPES.IOS]: initialContent }
        : initialContent;
      if (inAppContent.updateContent) {
        const currentContent = inAppContent.getDeviceContent?.(inAppContent.activeDevice);
        const newContent = contentToUpdate[inAppContent.activeDevice] || contentToUpdate[DEVICE_TYPES.ANDROID] || '';
        if (currentContent !== newContent) {
          inAppContent.updateContent(newContent, true);
        }
      }
    }
  }, [initialContent, isEmailVariant, isInAppVariant]);
  // Handle context change for tag API calls
  // If variant is INAPP, use SMS layout; otherwise use the channel (EMAIL)
  const handleContextChange = useCallback((contextData) => {
    // If onContextChange is provided, use it instead of making our own API call
    // This prevents duplicate API calls when parent component handles tag fetching
    if (onContextChange) {
      onContextChange(contextData);
      return;
    }

    // Only make API call if onContextChange is not provided and globalActions is available
    if (!globalActions || !location) {
      return;
    }

    const { type } = location.query || {};
    const tempData = (contextData || '').toLowerCase();
    const isEmbedded = type === EMBEDDED;
    const embedded = isEmbedded ? type : FULL;
    const context = tempData === ALL ? DEFAULT : tempData;

    // Determine layout: INAPP variant uses SMS, EMAIL variant uses EMAIL
    const layout = variant === HTML_EDITOR_VARIANTS.INAPP ? SMS : EMAIL;

    const query = {
      layout,
      type: TAG,
      context,
      embedded,
    };

    // Call the API via Redux action - this will trigger the saga which calls Api.fetchSchemaForEntity
    // The API endpoint will be: /meta/TAG?query={...}
    globalActions.fetchSchemaForEntity(query);
  }, [variant, globalActions, location, onContextChange]);

  // Destructure content properties for cleaner access throughout component
  const {
    activeDevice,
    keepContentSame,
    switchDevice,
    toggleContentSync,
    getDeviceContent,
    markAsSaved,
  } = content || {};

  const layout = useLayoutState({
    splitSizes: [50, 50],
    viewMode: 'desktop',
    mobileWidth: 375,
    isFullscreen: false,
  });

  // Get current content for validation based on variant
  const currentContent = variant === HTML_EDITOR_VARIANTS.EMAIL
    ? content?.content
    : getDeviceContent?.(activeDevice);

  // Create message formatter for sanitizer
  const formatSanitizerMessage = useCallback((messageKey, values = {}) => {
    // Map sanitizer message keys to our message structure
    const messageMap = {
      'sanitizer.invalidInput': messages.sanitizer.invalidInput,
      'sanitizer.invalidInputNonEmpty': messages.sanitizer.invalidInputNonEmpty,
      'sanitizer.sanitizationFailed': messages.sanitizer.sanitizationFailed,
      'sanitizer.dangerousProtocolDetected': messages.sanitizer.dangerousProtocolDetected,
      'sanitizer.emailMultimediaNotSupported': messages.sanitizer.emailMultimediaNotSupported,
      'sanitizer.emailPositioningNotSupported': messages.sanitizer.emailPositioningNotSupported,
      'sanitizer.emailModernCssLimited': messages.sanitizer.emailModernCssLimited,
      'sanitizer.emailViewportUnitsNotSupported': messages.sanitizer.emailViewportUnitsNotSupported,
      'sanitizer.mobileTablesNotFriendly': messages.sanitizer.mobileTablesNotFriendly,
      'sanitizer.mobileRelativeFontSizes': messages.sanitizer.mobileRelativeFontSizes,
      'sanitizer.mobileFixedWidthsProblematic': messages.sanitizer.mobileFixedWidthsProblematic,
      'sanitizer.mobileRelativeUnits': messages.sanitizer.mobileRelativeUnits,
      'sanitizer.productionValidHtml': messages.sanitizer.productionValidHtml,
      'sanitizer.productionSanitized': messages.sanitizer.productionSanitized,
      'sanitizer.productionInlineCss': messages.sanitizer.productionInlineCss,
      'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent,
    };

    const messageObj = messageMap[messageKey];
    return messageObj ? intl.formatMessage(messageObj, values) : messageKey;
  }, [intl]);

  // Create message formatter for validator
  const formatValidatorMessage = useCallback((messageKey, values = {}) => {
    // Map validator message keys to our message structure
    const messageMap = {
      'validator.validationFailed': messages.validator.validationFailed,
      'validator.liquidValidationFailed': messages.validator.liquidValidationFailed,
      'validator.unsafeProtocolDetected': messages.validator.unsafeProtocolDetected,
      'validator.scriptTagsDetected': messages.validator.scriptTagsDetected,
      'validator.outlookIncompatible': messages.validator.outlookIncompatible,
      'validator.emailCssUnsupported': messages.validator.emailCssUnsupported,
      'validator.mobileIncompatible': messages.validator.mobileIncompatible,
      'validator.largeImageDetected': messages.validator.largeImageDetected,
      'validator.unclosedCssRule': messages.validator.unclosedCssRule,
      'validator.emptyCssRule': messages.validator.emptyCssRule,
      'validator.cssValidationFailed': messages.validator.cssValidationFailed,
    };

    const messageObj = messageMap[messageKey];
    return messageObj ? intl.formatMessage(messageObj, values) : messageKey;
  }, [intl]);

  const validation = useValidation(currentContent, variant, {
    enableRealTime: true,
    debounceMs: 500,
    enableSanitization: true,
    securityLevel: 'standard',
    apiValidationErrors, // Pass API validation errors to merge with client-side validation
    isFullMode, // Skip liquid validation in standalone/full mode
  }, formatSanitizerMessage, formatValidatorMessage);

  // Expose validation and content state via ref
  useImperativeHandle(ref, () => ({
    getValidation: () => validation,
    getContent: () => currentContent,
    isContentEmpty: () => !currentContent || currentContent.trim() === '',
    getIssueCounts: () => {
      if (!validation || typeof validation.getAllIssues !== 'function') {
        return { errors: 0, warnings: 0, total: 0 };
      }
      return countIssuesBySeverity(validation.getAllIssues());
    },
    getValidationState: () => ({
      isValidating: validation?.isValidating || false,
      hasErrors: validation?.hasBlockingErrors || false,
      issueCounts: !validation || typeof validation.getAllIssues !== 'function'
        ? { errors: 0, warnings: 0, total: 0 }
        : countIssuesBySeverity(validation.getAllIssues()),
    })
    ,
  }), [validation, currentContent, apiValidationErrors]); // Include apiValidationErrors so ref methods return updated counts

  // Use ref to store callback to avoid infinite loops (callback in deps would cause re-runs)
  const onValidationChangeRef = useRef(onValidationChange);
  useEffect(() => {
    onValidationChangeRef.current = onValidationChange;
  }, [onValidationChange]);

  // Track last sent validation state to prevent duplicate updates
  const lastSentValidationStateRef = useRef(null);

  // Store validation ref to access current value without triggering re-renders
  const validationRef = useRef(validation);
  validationRef.current = validation;

  // Extract STABLE primitive values from validation for dependency array
  const isValidating = validation?.isValidating;
  const validationTotalErrors = validation?.summary?.totalErrors || 0;
  const validationTotalWarnings = validation?.summary?.totalWarnings || 0;
  const validationLastValidated = validation?.lastValidated?.getTime?.() || null;
  // Only Rule Group #1 (Input & Sanitization) blocks; use for UI gating (Done/Update/Preview/Test)
  const validationHasBlockingErrors = validation?.hasBlockingErrors || false;

  // Notify parent component when validation state changes
  useEffect(() => {
    if (!onValidationChangeRef.current) {
      return;
    }

    // Skip if still validating (wait for validation to complete)
    if (isValidating) {
      return;
    }

    // Calculate issue counts: Errors (blocking) and Warnings (non-blocking)
    const calculateIssueCounts = () => {
      const currentValidation = validationRef.current;
      if (!currentValidation || typeof currentValidation.getAllIssues !== 'function') {
        return { errors: 0, warnings: 0, total: 0 };
      }
      return countIssuesBySeverity(currentValidation.getAllIssues());
    };

    const issueCounts = calculateIssueCounts();
    const isContentEmpty = !currentContent || currentContent.trim() === '';

    // hasErrors = only Rule Group #1 (Input & Sanitization) - gates Done/Update/Preview/Test
    const newState = {
      isContentEmpty,
      issueCounts,
      validationComplete: true,
      hasErrors: validationRef.current?.hasBlockingErrors ?? false,
    };

    // Only call callback if state actually changed (prevent infinite loops)
    const lastState = lastSentValidationStateRef.current;
    const hasChanged = !lastState
      || lastState.isContentEmpty !== newState.isContentEmpty
      || lastState.validationComplete !== newState.validationComplete
      || lastState.hasErrors !== newState.hasErrors
      || lastState.issueCounts?.total !== newState.issueCounts?.total
      || lastState.issueCounts?.errors !== newState.issueCounts?.errors
      || lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;

    if (hasChanged) {
      lastSentValidationStateRef.current = newState;
      onValidationChangeRef.current(newState);
    }
  }, [isValidating, validationTotalErrors, validationTotalWarnings, validationLastValidated, validationHasBlockingErrors, currentContent, apiValidationErrors]);

  // Send initial state on mount to ensure parent has correct initial button state
  const hasInitializedRef = useRef(false);
  useEffect(() => {
    if (hasInitializedRef.current || !onValidationChangeRef.current) {
      return;
    }
    hasInitializedRef.current = true;

    // Send initial state with validationComplete=false to indicate validation pending
    const isContentEmpty = !currentContent || currentContent.trim() === '';
    onValidationChangeRef.current({
      isContentEmpty,
      issueCounts: { errors: 0, warnings: 0, total: 0 },
      validationComplete: false, // Validation hasn't run yet
      hasErrors: false,
    });
  }, [currentContent]); // Only depend on currentContent to run on initial content load

  // Force validation state recalculation when API validation errors change
  // This ensures that API errors are included in issue counts and displayed in ValidationErrorDisplay
  useEffect(() => {
    if (!onValidationChangeRef.current) {
      return;
    }

    // Skip if still validating (wait for validation to complete)
    if (validation?.isValidating) {
      return;
    }

    // Recalculate issue counts (Errors + Warnings) including API errors
    const issueCounts = !validation || typeof validation.getAllIssues !== 'function'
      ? { errors: 0, warnings: 0, total: 0 }
      : countIssuesBySeverity(validation.getAllIssues());
    const isContentEmpty = !currentContent || currentContent.trim() === '';

    const newState = {
      isContentEmpty,
      issueCounts,
      validationComplete: true,
      hasErrors: validation?.hasBlockingErrors ?? false,
    };

    const lastState = lastSentValidationStateRef.current;
    const hasChanged = !lastState
      || lastState.hasErrors !== newState.hasErrors
      || lastState.issueCounts?.total !== newState.issueCounts?.total
      || lastState.issueCounts?.errors !== newState.issueCounts?.errors
      || lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;

    if (hasChanged) {
      lastSentValidationStateRef.current = newState;
      onValidationChangeRef.current(newState);
    }
  }, [apiValidationErrors, validation, currentContent, onValidationChangeRef]);

  // Handle label insertion at cursor position
  // Note: This is called for notification purposes only when tag is inserted via CodeEditorPane
  // The actual insertion happens in CodeEditorPane.handleTagSelect
  const handleLabelInsert = useCallback((label, position) => {
    // If position is explicitly null, it means the editor wasn't ready when tag was selected
    // In this case, CodeEditorPane couldn't insert the tag, so we should try here
    if (position === null) {
      // With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
      const activeEditorRef = getActiveEditorRef();
      const editor = activeEditorRef.current;

      if (!editor) {
        CapNotification.warning({
          message: intl.formatMessage(messages.labelInsertError),
          description: intl.formatMessage(messages.editorNotReady),
          duration: 3,
        });
        return;
      }

      // Check if the required methods exist
      if (typeof editor?.insertText !== 'function') {
        CapNotification.error({
          message: intl.formatMessage(messages.labelInsertError),
          description: intl.formatMessage(messages.editorMethodNotAvailable),
          duration: 4,
        });
        return;
      }

      try {
        // Get current cursor position
        const cursor = typeof editor?.getCursor === 'function' ? editor.getCursor() : 0;

        // Insert label at cursor position
        editor.insertText(label, cursor);

        // Focus the editor if focus method is available
        editor?.focus?.();

        // Show success notification
        CapNotification.success({
          message: intl.formatMessage(messages.labelInserted),
          description: intl.formatMessage(messages.labelInsertedDescription, { label }),
          duration: 2,
        });
      } catch (error) {
        CapNotification.error({
          message: intl.formatMessage(messages.labelInsertError),
          description: error.message,
          duration: 4,
        });
      }
    } else {
      // Tag was already inserted by CodeEditorPane (position is a valid number)
      // Just show success notification - no need to access editor
      CapNotification.success({
        message: intl.formatMessage(messages.labelInserted),
        description: intl.formatMessage(messages.labelInsertedDescription, { label }),
        duration: 2,
      });
    }
  }, [intl, getActiveEditorRef]);

  // Handle save action
  const handleSave = useCallback(() => {
    try {
      const { html, css, javascript } = content?.content || {};
      const contentToSave = { html, css, javascript };

      if (onSave) {
        onSave(contentToSave);
      }

      markAsSaved?.();

      CapNotification.success({
        message: intl.formatMessage(messages.contentSaved),
        duration: 2,
      });
    } catch (error) {
      CapNotification.error({
        message: intl.formatMessage(messages.saveError),
        description: error.message,
        duration: 4,
      });
    }
  }, [content, onSave, intl, markAsSaved]);

  // Handle validation error click - navigate to error line in editor
  const handleValidationErrorClick = useCallback((error) => {
    const activeEditorRef = getActiveEditorRef();
    const editorInstance = activeEditorRef.current;
    const { line, column = 1 } = error || {};

    // Notify parent that user acknowledged errors by clicking redirection icon
    // This enables the buttons even if we can't navigate to a specific line
    if (onErrorAcknowledged) {
      onErrorAcknowledged();
    }

    if (editorInstance) {
      try {
        // If line number exists, navigate to it; otherwise just focus the editor
        if (line && editorInstance?.navigateToLine) {
          editorInstance.navigateToLine(line, column);
        } else {
          // For API errors without line numbers, just focus the editor
          editorInstance?.focus?.();
        }
      } catch (err) {
        // Fallback: just focus the editor
        editorInstance?.focus?.();
      }
    }
  }, [getActiveEditorRef, onErrorAcknowledged]);

  // Handle fullscreen modal
  const handleOpenFullscreen = useCallback(() => {
    setIsFullscreenModalOpen(true);
  }, []);

  const handleCloseFullscreen = useCallback(() => {
    setIsFullscreenModalOpen(false);
  }, []);

  // Context value for providers
  const contextValue = useMemo(() => ({
    variant,
    content,
    layout,
    validation,
    editorRef: getActiveEditorRef(),
    handleLabelInsert,
    handleSave,
    readOnly,
    isFullscreenMode: isFullscreenModalOpen,
    // InApp specific context
    ...(variant === HTML_EDITOR_VARIANTS.INAPP && {
      activeDevice,
      keepContentSame,
      switchDevice,
      toggleContentSync,
      getDeviceContent,
      layoutType,
    }),
  }), [
    variant,
    content,
    layout,
    validation,
    getActiveEditorRef,
    handleLabelInsert,
    handleSave,
    readOnly,
    isFullscreenModalOpen,
    activeDevice,
    keepContentSame,
    switchDevice,
    toggleContentSync,
    getDeviceContent,
    layoutType,
  ]);

  // Loading state
  if (!content || !layout) {
    return (
      <CapRow useLegacy className="html-editor-loading">
        <CapSpin size="large" tip={intl.formatMessage(messages.initializing)} />
      </CapRow>
    );
  }

  // Add library-mode class when not in full mode
  // Note: isFullMode defaults to true, so library mode is when isFullMode === false
  const isLibraryMode = isFullMode === false;
  const editorClassName = `html-editor html-editor--${variant} ${isLibraryMode ? 'html-editor--library-mode' : ''} ${className}`.trim();

  return (
    <EditorProvider value={contextValue}>
      <div className={editorClassName} {...props}>
        {/* Editor Toolbar - Conditional based on variant */}
        {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
          <EditorToolbar
            showFullscreenButton={showFullscreenButton}
            onLabelInsert={handleLabelInsert}
            onSave={handleSave}
            onToggleFullscreen={handleOpenFullscreen}
          />
        ) : (
          /* InApp variant: Device Toggle + Toolbar combined */
          <CapRow useLegacy className="html-editor__header">
            <DeviceToggle
              activeDevice={activeDevice}
              onDeviceChange={switchDevice}
              keepContentSame={keepContentSame}
              onKeepContentSameChange={toggleContentSync}
            />
            <EditorToolbar
              showFullscreenButton={showFullscreenButton}
              onLabelInsert={handleLabelInsert}
              onSave={handleSave}
              onToggleFullscreen={handleOpenFullscreen}
              variant={variant}
              showTitle={false} // Hide title in InApp variant
            />
          </CapRow>
        )}

        {/* Main Content Area - Split Container */}
        <CapRow type="flex" vertical noWrap className="html-editor-content">
          <SplitContainer className="html-editor-split-container">
            {/* Code Editor Pane */}
            <CodeEditorPane
              ref={mainEditorRef}
              readOnly={readOnly}
              onLabelInsert={handleLabelInsert}
              onErrorClick={handleValidationErrorClick}
              tags={tags}
              injectedTags={injectedTags}
              location={location}
              eventContextTags={eventContextTags}
              waitEventContextTags={waitEventContextTags}
              selectedOfferDetails={selectedOfferDetails}
              channel={channel}
              userLocale={userLocale}
              moduleFilterEnabled={moduleFilterEnabled}
              onTagContextChange={onTagContextChange}
              onTagSelect={onTagSelect}
              onContextChange={handleContextChange}
            />

            {/* Preview Pane */}
            <PreviewPane />
          </SplitContainer>
        </CapRow>

        {/* Fullscreen Modal */}
        <CapModal
          title={null} // Remove modal header to avoid duplication
          visible={isFullscreenModalOpen}
          onCancel={handleCloseFullscreen}
          footer={null}
          maskClosable
          centered
          closable={false}
          width="93vw"
          className="html-editor-fullscreen-modal"
          classNames={{
            container: 'html-editor-fullscreen-modal-container',
          }}
        >
          <CapRow className={`html-editor-fullscreen html-editor-fullscreen--${variant} ${isLibraryMode ? 'html-editor-fullscreen--library-mode' : ''}`}>
            {/* Editor Toolbar - Conditional based on variant */}
            {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
              <EditorToolbar
                showFullscreenButton // Show fullscreen button in modal to allow closing
                onLabelInsert={handleLabelInsert}
                onSave={handleSave}
                isFullscreenMode
                onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
              />
            ) : (
              /* InApp variant: Device Toggle + Toolbar combined in fullscreen */
              <CapRow className="html-editor__header html-editor__header--fullscreen">
                <DeviceToggle
                  activeDevice={activeDevice}
                  onDeviceChange={switchDevice}
                  keepContentSame={keepContentSame}
                  onKeepContentSameChange={toggleContentSync}
                />
                <EditorToolbar
                  showFullscreenButton // Show fullscreen button in modal to allow closing
                  onLabelInsert={handleLabelInsert}
                  onSave={handleSave}
                  isFullscreenMode
                  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
                  variant={variant}
                  showTitle={false} // Hide title in InApp variant
                />
              </CapRow>
            )}

            {/* Main Content Area - Split Container */}
            <CapRow className="html-editor-content-fullscreen">
              <SplitContainer className="html-editor-split-container-fullscreen">
                {/* Code Editor Pane */}
                <CodeEditorPane
                  ref={modalEditorRef}
                  readOnly={readOnly}
                  isFullscreenMode
                  onLabelInsert={handleLabelInsert}
                  onErrorClick={handleValidationErrorClick}
                  tags={tags}
                  injectedTags={injectedTags}
                  location={location}
                  eventContextTags={eventContextTags}
                  waitEventContextTags={waitEventContextTags}
                  selectedOfferDetails={selectedOfferDetails}
                  channel={channel}
                  userLocale={userLocale}
                  moduleFilterEnabled={moduleFilterEnabled}
                  onTagContextChange={onTagContextChange}
                />

                {/* Preview Pane */}
                <PreviewPane isFullscreenMode isModalContext />
              </SplitContainer>
            </CapRow>
          </CapRow>
        </CapModal>
      </div>
    </EditorProvider>
  );
});

HTMLEditor.propTypes = {
  intl: intlShape.isRequired,
  variant: PropTypes.oneOf(Object.values(HTML_EDITOR_VARIANTS)), // Added variant prop
  layoutType: PropTypes.string, // Layout type for InApp variant
  initialContent: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.objectOf(PropTypes.string), // Per-device content for INAPP variant
  ]),
  onSave: PropTypes.func,
  onContentChange: PropTypes.func,
  className: PropTypes.string,
  readOnly: PropTypes.bool,
  showFullscreenButton: PropTypes.bool,
  autoSave: PropTypes.bool,
  autoSaveInterval: PropTypes.number,
  // 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
  onTagSelect: PropTypes.func,
  onContextChange: PropTypes.func, // Deprecated: use globalActions instead
  globalActions: PropTypes.object,
  isFullMode: PropTypes.bool, // Full mode vs library mode
  onErrorAcknowledged: PropTypes.func, // Callback when user clicks redirection icon to acknowledge errors
  onValidationChange: PropTypes.func, // Callback when validation state changes
  apiValidationErrors: PropTypes.shape({
    liquidErrors: PropTypes.arrayOf(PropTypes.string),
    standardErrors: PropTypes.arrayOf(PropTypes.string),
  }), // API validation errors from validateLiquidTemplateContent
};

HTMLEditor.defaultProps = {
  variant: HTML_EDITOR_VARIANTS.EMAIL, // Default to email variant
  layoutType: null,
  initialContent: null, // Will use default from useEditorContent hook
  onSave: null,
  onContentChange: null,
  className: '',
  readOnly: false,
  showFullscreenButton: true,
  autoSave: true,
  autoSaveInterval: 30000,
  // Tag-related defaults - tags are fetched and managed by parent component
  tags: [],
  injectedTags: {},
  location: null,
  eventContextTags: [],
  waitEventContextTags: {},
  selectedOfferDetails: [],
  channel: null,
  userLocale: 'en',
  moduleFilterEnabled: true,
  onTagContextChange: null, // Parent component should provide this
  onTagSelect: null,
  onContextChange: null,
  globalActions: null, // Redux actions for API calls
  isFullMode: true, // Default to full mode
  onErrorAcknowledged: null, // Callback when user clicks redirection icon to acknowledge errors
  onValidationChange: null, // Callback when validation state changes
  apiValidationErrors: null, // API validation errors from validateLiquidTemplateContent
};

// Export with injectIntl - HTMLEditor now uses forwardRef internally
export default injectIntl(HTMLEditor);
