/**
 * FieldInitializer - Handles field initialization with retry logic
 *
 * Replaces the complex initializeNewFieldWithRetry function with a
 * cleaner, more maintainable approach using async/await.
 */

import { FIELD_STATES } from './FieldStateManager';

export class FieldInitializer {
  constructor(stateManager, editorInitializer, toolbarConfigManager) {
    this.stateManager = stateManager;
    this.editorInitializer = editorInitializer;
    this.toolbarConfigManager = toolbarConfigManager;
    this.debounceTimers = new Map();
  }

  /**
   * Initialize a field with debouncing and retry logic
   * @param {string} id - Field ID
   * @param {Element} textarea - Textarea element
   * @param {number} maxRetries - Maximum retry attempts
   */
  initializeField(id, textarea, maxRetries = 3) {
    // Skip if already initialized or processing
    if (this.stateManager.isInitialized(id)) {
      console.log(`Field ${id} already initialized, skipping`);
      return;
    }

    if (this.stateManager.isProcessing(id)) {
      console.log(`Field ${id} already being processed, skipping`);
      return;
    }

    // Clear any existing debounce timer
    if (this.debounceTimers.has(id)) {
      clearTimeout(this.debounceTimers.get(id));
    }

    // Debounce initialization
    const timer = setTimeout(() => {
      this.debounceTimers.delete(id);
      this.performInitialization(id, textarea, 0, maxRetries);
    }, 50);

    this.debounceTimers.set(id, timer);
  }

  /**
   * Perform the actual initialization with retry support
   * @param {string} id - Field ID
   * @param {Element} textarea - Textarea element
   * @param {number} attempt - Current attempt number
   * @param {number} maxRetries - Maximum retries
   */
  performInitialization(id, textarea, attempt, maxRetries) {
    try {
      // Double-check state (race condition protection)
      if (this.stateManager.isInitialized(id) || this.stateManager.isProcessing(id)) {
        console.log(`Field ${id} state changed during debounce, aborting`);
        return;
      }

      // Re-validate textarea
      const currentTextarea = document.getElementById(id);
      if (!this.isFieldValid(id, currentTextarea)) {
        if (attempt < maxRetries) {
          console.log(`Field ${id} not ready, scheduling retry ${attempt + 1}/${maxRetries}`);
          this.scheduleRetry(id, currentTextarea, attempt, maxRetries);
          return;
        }

        console.warn(`Field ${id} failed validation after ${maxRetries} retries`);
        this.stateManager.markAsFailed(id, new Error('Validation failed'), attempt);
        return;
      }

      // Mark as processing
      this.stateManager.markAsProcessing(id);

      // Determine field type and initialize
      const $textarea = jQuery(currentTextarea);
      const isACFField = $textarea.closest('.acf-field').length > 0;
      const isJetTextarea = $textarea.hasClass('cx-ui-textarea');

      if (isACFField) {
        this.initializeACFField(id, $textarea);
      } else if (isJetTextarea) {
        this.initializeJetTextareaField(id, $textarea);
      } else {
        this.initializeStandardField(id, $textarea);
      }

      // Mark as initialized
      this.stateManager.markAsInitialized(id);
      console.log(`Successfully initialized field: ${id}`);
    } catch (error) {
      console.error(`Failed to initialize field ${id} (attempt ${attempt + 1}):`, error);

      if (attempt < maxRetries) {
        this.scheduleRetry(id, textarea, attempt, maxRetries, true);
      } else {
        this.stateManager.markAsFailed(id, error, attempt);
        console.error(`Field ${id} failed to initialize after ${maxRetries + 1} attempts`);
      }
    }
  }

  /**
   * Schedule a retry for initialization
   * @param {string} id - Field ID
   * @param {Element} textarea - Textarea element
   * @param {number} attempt - Current attempt
   * @param {number} maxRetries - Maximum retries
   * @param {boolean} isError - Whether this is an error retry
   */
  scheduleRetry(id, textarea, attempt, maxRetries, isError = false) {
    // Exponential backoff: 200ms, 400ms, 800ms (longer for errors)
    const baseDelay = isError ? 500 : 200;
    const delay = baseDelay * Math.pow(2, attempt);

    const timer = setTimeout(() => {
      this.performInitialization(id, textarea, attempt + 1, maxRetries);
    }, delay);

    this.stateManager.setRetryTimer(id, timer);
  }

  /**
   * Check if a field is valid for initialization
   * @param {string} id - Field ID
   * @param {Element} textarea - Textarea element
   * @returns {boolean}
   */
  isFieldValid(id, textarea) {
    // Basic checks
    if (!textarea || !id) return false;

    // Exclusion check
    if (this.stateManager.shouldExclude(id, textarea)) {
      return false;
    }

    // DOM attachment check
    if (!document.body.contains(textarea)) {
      console.log(`Field ${id} not attached to DOM`);
      return false;
    }

    // Skip if TinyMCE already has an editor for this field (e.g. initialized by JetEngine)
    if (window.tinymce?.get(id)) {
      console.log(`Field ${id} already has a TinyMCE editor instance, skipping`);
      return false;
    }

    // Field type validation
    const $textarea = jQuery(textarea);
    const isACFField = $textarea.closest('.acf-field.acf-field-textarea').length > 0;
    const isJetField = $textarea.closest('.cx-control-textarea').length > 0;

    if (!isACFField && !isJetField) {
      // Not an ACF or JetEngine field, check if it's a standard editor
      const $wrap = jQuery(`#wp-${id}-wrap`);
      if ($wrap.length === 0 && !window.tinyMCEPreInit?.mceInit?.[id]) {
        console.log(`Field ${id} is not a recognized editor field`);
        return false;
      }
    }

    return true;
  }

  /**
   * Initialize an ACF field
   * @param {string} id - Field ID
   * @param {jQuery} $textarea - jQuery textarea element
   */
  initializeACFField(id, $textarea) {
    // Get base configuration
    const baseConfig =
      window.tinyMCEPreInit?.mceInit?.acf_content ||
      window.tinyMCEPreInit?.mceInit?.options ||
      this.getFallbackConfig();

    if (!baseConfig) {
      throw new Error(`No configuration available for ACF field ${id}`);
    }

    const $field = $textarea.closest('.acf-field');

    // Build editor configuration
    let editorConfig = this.editorInitializer.getBaseConfig(id, { ...baseConfig });

    // Apply toolbar configuration
    const toolbar = this.toolbarConfigManager.getToolbarFromField($field);
    if (toolbar) {
      editorConfig = this.toolbarConfigManager.configureFromToolbar(editorConfig, toolbar);
    }

    // Set selector and ensure interactivity
    editorConfig.selector = `#${id}`;
    editorConfig.content_css = baseConfig.content_css;
    editorConfig.plugins = baseConfig.plugins;
    editorConfig.theme = baseConfig.theme;
    editorConfig.skin = baseConfig.skin;
    editorConfig.skin_url = baseConfig.skin_url;
    editorConfig.toolbar = editorConfig.toolbar || baseConfig.toolbar;
    editorConfig.menubar = baseConfig.menubar !== undefined ? baseConfig.menubar : false;
    editorConfig.statusbar = baseConfig.statusbar !== undefined ? baseConfig.statusbar : false;
    editorConfig.document_base_url = baseConfig.document_base_url;
    editorConfig.theme_url = baseConfig.theme_url;
    editorConfig.tinymce_script_src = baseConfig.tinymce_script_src;
    editorConfig.readonly = false;
    editorConfig.disabled = false;

    // Determine if inline editor
    const isWysiwyg = $textarea.hasClass('acf-wysiwyg');
    console.log(`Initializing ACF field ${id} - WYSIWYG: ${isWysiwyg}, Inline: ${!isWysiwyg}`);

    this.editorInitializer.initEditor(`#${id}`, editorConfig, !isWysiwyg);
  }

  /**
   * Initialize a JetEngine textarea field as an inline editor.
   * Replaces the <textarea> with a contenteditable <div> since
   * TinyMCE inline mode requires a block element.
   * @param {string} id - Field ID
   * @param {jQuery} $textarea - jQuery textarea element
   */
  initializeJetTextareaField(id, $textarea) {
    const textarea = $textarea[0];

    // Create a div to replace the textarea for inline editing
    const div = document.createElement('div');
    div.id = id;
    div.className = textarea.className + ' erte-inline-editor';
    div.innerHTML = textarea.value || '';

    // Hide the original textarea and insert the div
    textarea.id = id + '__original';
    textarea.style.display = 'none';
    textarea.parentNode.insertBefore(div, textarea);

    // Sync content back to the hidden textarea on change
    const baseConfig =
      Object.values(window.tinyMCEPreInit?.mceInit || {})[0] ||
      this.getFallbackConfig();

    const editorConfig = this.editorInitializer.getBaseConfig(id, { ...baseConfig });
    editorConfig.selector = `#${id}`;
    editorConfig.inline = true;
    editorConfig.setup = (editor) => {
      editor.on('input change keyup', () => {
        textarea.value = editor.getContent();
      });
      editor.on('blur', () => {
        textarea.value = editor.getContent();
      });
    };

    this.editorInitializer.initEditor(`#${id}`, editorConfig, true);
  }

  /**
   * Initialize a standard WordPress editor field
   * @param {string} id - Field ID
   * @param {jQuery} $textarea - jQuery textarea element
   */
  initializeStandardField(id, $textarea) {
    const config = window.tinyMCEPreInit?.mceInit?.[id] || this.getFallbackConfig();

    const baseConfig = this.editorInitializer.getBaseConfig(id, config);
    const isInline = id === 'excerpt' || $textarea.hasClass('inline-editor');

    // Check for toolbar customization
    const $wrap = jQuery(`#wp-${id}-wrap`);
    const $editorContainer = $wrap.find(`#wp-${id}-editor-container`);
    const toolbar = $editorContainer.data('toolbar');

    let editorConfig = { ...baseConfig };
    if (toolbar) {
      editorConfig = this.toolbarConfigManager.configureFromToolbar(editorConfig, toolbar);
    }

    this.editorInitializer.initEditor(`#${id}`, editorConfig, isInline);
  }

  /**
   * Get fallback configuration when no config is available
   * @returns {Object}
   */
  getFallbackConfig() {
    const existingConfig = Object.values(window.tinyMCEPreInit?.mceInit || {})[0];

    return {
      plugins: 'tdblocks tdstyles tdlink tdimage',
      toolbar: 'bold italic | bullist numlist | tdlink tdimage',
      height: 200,
      menubar: false,
      statusbar: false,
      readonly: false,
      disabled: false,
      content_css: existingConfig?.content_css,
      theme: existingConfig?.theme || 'silver',
      skin: existingConfig?.skin,
      skin_url: existingConfig?.skin_url,
      document_base_url: existingConfig?.document_base_url,
      theme_url: existingConfig?.theme_url,
      tinymce_script_src: existingConfig?.tinymce_script_src
    };
  }

  /**
   * Force initialize a field bypassing readiness checks
   * @param {string} id - Field ID
   * @param {Element} textarea - Textarea element
   */
  forceInitialize(id, textarea) {
    console.log(`Force initializing field ${id}`);

    // Clear any pending retries
    const fieldData = this.stateManager.getFieldData(id);
    if (fieldData?.retryTimer) {
      clearTimeout(fieldData.retryTimer);
    }

    // Reset state
    this.stateManager.removeField(id);

    // Perform initialization directly
    this.performInitialization(id, textarea, 0, 0);
  }

  /**
   * Retry all failed fields
   */
  retryFailedFields() {
    const failedFields = this.stateManager.getRetryableFields();
    console.log(`Retrying ${failedFields.length} failed fields`);

    failedFields.forEach(({ id, retryCount }) => {
      const textarea = document.getElementById(id);
      if (textarea) {
        console.log(`Retrying field ${id} (previous attempts: ${retryCount})`);
        this.stateManager.removeField(id);
        this.initializeField(id, textarea);
      } else {
        this.stateManager.removeField(id);
      }
    });
  }

  /**
   * Clean up all debounce timers
   */
  cleanup() {
    this.debounceTimers.forEach(timer => clearTimeout(timer));
    this.debounceTimers.clear();
  }
}

export default FieldInitializer;
