(function () {
    if (typeof Vue === 'undefined') return;

    Vue.component('wpcfto_codemirror', {
        props: ['fields', 'field_label', 'field_id', 'field_name', 'field_value'],
        template: `
            <div class="wpcfto_generic_field wpcfto_generic_field_codemirror">
                <wpcfto_fields_aside_before 
                    :fields="fields" 
                    :field_label="field_label"
                    :required="fields.required === true">
                </wpcfto_fields_aside_before>

                <div class="wpcfto-field-content">
                    <!-- Language Label -->
                    <div v-if="showModeLabel && !allowModeSwitch" class="codemirror-mode-label">
                        {{ readableMode }}
                    </div>

                    <!-- Language Switcher -->
                    <div v-if="allowModeSwitch" class="codemirror-mode-switcher">
                        <select v-model="currentMode">
                            <option v-for="(label, mode) in languageLabels" :value="mode">{{ label }}</option>
                        </select>
                    </div>

                    <!-- Editor (textarea transformed by CodeMirror) -->
                    <textarea :id="field_id" :name="field_name"></textarea>

                    <!-- Hidden mirror for required + form submit -->
                    <input
                      type="text"
                      :name="field_name"
                      :value="value"
                      :required="fields && fields.required === true"
                      tabindex="-1"
                      readonly
                      style="position:absolute;opacity:0;height:0;width:0;pointer-events:none;"
                    />
                </div>

                <wpcfto_fields_aside_after :fields="fields"></wpcfto_fields_aside_after>
            </div>
        `,
        data() {
          return {
              editor: null,
              value: '',
              domObserver: null,
              currentMode: this.fields.mode || 'javascript',
              showModeLabel: typeof this.fields.show_language_label !== 'undefined' ? this.fields.show_language_label : true,
              allowModeSwitch: typeof this.fields.allow_language_switch !== 'undefined' ? this.fields.allow_language_switch : false,
              languageLabels: this.fields.language_labels || {
                  'javascript': 'JavaScript',
                  'json': 'JSON',
                  'css': 'CSS',
                  'htmlmixed': 'HTML',
                  'text/plain': 'Plain Text'
              },
              _origConsoleError: null,
              _origConsoleWarn: null,
          };
        },
        mounted() {
          this.$nextTick(() => {
              // Silence extremely noisy passive-listener warnings (harmless browser noise)
              if (!this._origConsoleError && typeof console !== 'undefined') {
                  this._origConsoleError = console.error;
                  this._origConsoleWarn = console.warn;
                  const drop = (fn) => function(){
                      const msg = arguments && arguments[0];
                      if (typeof msg === 'string' && msg.indexOf('Unable to preventDefault inside passive event listener invocation.') !== -1) return;
                      return fn.apply(console, arguments);
                  };
                  try { console.error = drop(this._origConsoleError); } catch(e){}
                  try { console.warn  = drop(this._origConsoleWarn); } catch(e){}
              }
              this.$nextTick(() => {
                  const selector = '#' + this.field_id;
                  this.waitForElementVisible(selector, () => {
                      const fieldObj = (this.fields && typeof this.fields === 'object') ? this.fields : {};
                      let rawValue = '';
                      if (typeof this.field_value === 'string' && !this.isPathString(this.field_value)) {
                          rawValue = this.field_value;
                      } else if (fieldObj && typeof fieldObj.value === 'string') {
                          rawValue = fieldObj.value;
                      } else {
                          rawValue = '';
                      }
                      this.value = this.formatCode(rawValue, fieldObj.mode || this.currentMode);

                      this.initCodeMirror({
                          mode: fieldObj.mode || this.currentMode,
                          theme: fieldObj.theme || 'default'
                      });

                      this.$emit('wpcfto-get-value', this.value);

                      // Nudge global validators after init
                      this.$nextTick(() => {
                          try { if (typeof this.validateField === 'function') this.validateField(); } catch(e){}
                      });

                      this.refreshOnTabActivation();
                      this.addMutationObserver();
                      this.initResizeObserver();
                  });
              });
          });
        },
        methods: {
            isPathString(str) {
                return typeof str === 'string' && /data\s*\[\s*['\"]/i.test(str);
            },
            initResizeObserver() {
              const container = document.getElementById(this.field_id)?.closest('.wpcfto-field-content');
              if (!container || typeof ResizeObserver === 'undefined') return;
          
              const observer = new ResizeObserver(() => {
                  if (this.editor) this.editor.refresh();
              });
          
              observer.observe(container);
          
              // Cleanup in beforeDestroy()
              this.resizeObserver = observer;
            },
            waitForElementVisible(selector, callback, maxTries = 60, interval = 100) {
                let tries = 0;
                const check = () => {
                    const el = document.querySelector(selector);
                    if (el && window.getComputedStyle(el).display !== 'none') {
                        callback(el);
                    } else if (tries < maxTries) {
                        tries++;
                        setTimeout(check, interval);
                    } else {
                        console.warn(`[codemirror] Timeout waiting for visible element: ${selector}`);
                    }
                };
                check();
            },
            addMutationObserver() {
                const el = document.getElementById(this.field_id);
                if (!el || this.domObserver) return;

                this.domObserver = new MutationObserver((mutations) => {
                    for (let mutation of mutations) {
                        if (mutation.type === 'attributes' && ['style', 'class'].includes(mutation.attributeName)) {
                            const isVisible = window.getComputedStyle(el).display !== 'none';
                            if (isVisible && this.editor) {
                                setTimeout(() => this.editor.refresh(), 100);
                            }
                        }
                    }
                });

                this.domObserver.observe(el, {
                    attributes: true,
                    attributeFilter: ['style', 'class'],
                    subtree: false
                });
            },
            formatCode(code, mode) {
                if (!code || typeof code !== 'string') return code;

                try {
                    const normalizedMode = (mode || '').toLowerCase();

                    if (normalizedMode.includes('json')) {
                        const parsed = JSON.parse(code);
                        return JSON.stringify(parsed, null, 2);
                    }

                    if (normalizedMode.includes('javascript')) {
                        return this.prettifyJS(code);
                    }

                    if (normalizedMode.includes('css')) {
                        return this.prettifyCSS(code);
                    }

                    if (normalizedMode.includes('html') || normalizedMode.includes('xml')) {
                        return this.prettifyHTML(code);
                    }

                    return code;
                } catch (e) {
                    console.warn('Formatting failed:', e);
                    return code;
                }
            },
            prettifyJS(code) {
                try {
                    return js_beautify(code, {
                        indent_size: 2,
                        preserve_newlines: true,
                        end_with_newline: true
                    });
                } catch (e) {
                    return code;
                }
            },
            prettifyCSS(code) {
                return code.replace(/\{([^{}]*)\}/g, (match, p1) => {
                    return '{\n    ' + p1.replace(/;\s*/g, ';\n    ') + '\n}';
                });
            },
            prettifyHTML(code) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(code, 'text/html');
                if (doc.documentElement.innerHTML.trim() === '') return code;

                const serializer = new XMLSerializer();
                let formatted = serializer.serializeToString(doc);

                formatted = formatted
                    .replace(/(<([a-zA-Z]+)[^>]*>)(?![\t\n\f\r ])(?!<\/\2>)/g, '$1\n    ')
                    .replace(/(?<=<([a-zA-Z]+)[^>]*>)(.*?)(?=<\/\1>)/g, (_, tag, content) =>
                        content.replace(/\n/g, '\n    ')
                    )
                    .replace(/(>)(<\w)/g, '$1\n$2')
                    .replace(/(<.*?>)/g, '$1\n');

                return formatted;
            },
            refreshOnTabActivation() {
              const el = document.getElementById(this.field_id);
              if (!el) return;
          
              const tabPanel = el.closest('.wpcfto-tab');
          
              if (!tabPanel || tabPanel.classList.contains('active')) {
                  // Already active
                  setTimeout(() => {
                      if (this.editor) this.editor.refresh();
                  }, 100);
                  return;
              }
          
              const handler = (e) => {
                  if (e.detail?.id === tabPanel.id) {
                      console.log(`[codemirror] Tab shown: ${tabPanel.id}, refreshing editor`);
          
                      // Multiple refreshes to force layout re-calculation
                      setTimeout(() => {
                          if (this.editor) this.editor.refresh();
                      }, 100);
          
                      setTimeout(() => {
                          if (this.editor) this.editor.refresh();
                      }, 300);
          
                      setTimeout(() => {
                          if (this.editor) this.editor.refresh();
                      }, 800);
          
                      document.removeEventListener('wpcfto_tab_shown', handler);
                  }
              };
          
              document.addEventListener('wpcfto_tab_shown', handler);
            },
            initCodeMirror(fieldObj) {
                const textarea = document.getElementById(this.field_id);
                if (!textarea) return;

                // Ensure the original textarea has the correct value
                textarea.value = this.value;

                if (this.editor) return;

                // During CM construction, force non‑passive listeners on the editor DOM only
                const typesToFix = { touchstart:1, touchmove:1, wheel:1, mousewheel:1 };
                const origAdd = EventTarget.prototype.addEventListener;
                EventTarget.prototype.addEventListener = function(type, listener, options){
                    try {
                        if ((type in typesToFix) && this && (
                            (this.classList && this.classList.contains('CodeMirror')) ||
                            (this.closest && typeof this.closest === 'function' && this.closest('.CodeMirror'))
                        )){
                            if (options === undefined) options = { passive: false };
                            else if (typeof options === 'boolean') options = { capture: !!options, passive: false };
                            else if (typeof options === 'object') options = Object.assign({}, options, { passive: false });
                        }
                    } catch(e){}
                    return origAdd.call(this, type, listener, options);
                };
                try {
                    this.editor = CodeMirror.fromTextArea(textarea, {
                        mode: fieldObj?.mode || 'htmlmixed',
                        theme: fieldObj?.theme || 'default',
                        lineNumbers: true,
                        lineWrapping: true,
                        tabSize: 2,
                        autoCloseBrackets: true,
                        matchBrackets: true,
                        readOnly: false,
                        dragDrop: true,
                        autofocus: false,
                        viewportMargin: Infinity,
                        inputStyle: 'contenteditable',
                        scrollbarStyle: 'native',
                    });
                } finally {
                    // Always restore global addEventListener immediately after CM attaches its handlers
                    EventTarget.prototype.addEventListener = origAdd;
                }

                this.editor.setValue(this.value);

                // Strong focus sequence to prevent immediate blur from surrounding UI scripts
                try { this.editor.focus(); } catch(e){}
                setTimeout(() => { try { this.editor.focus(); } catch(e){} }, 10);
                setTimeout(() => { try { this.editor.refresh(); this.editor.focus(); } catch(e){} }, 50);

                // Ensure clicks focus the editor and mitigate passive-listener side effects
                const wrapper = this.editor.getWrapperElement();
                if (wrapper) {
                    // Reduce need for preventDefault by disabling native touch scroll here
                    wrapper.style.touchAction = 'none';
                    wrapper.style.webkitUserSelect = 'text';
                    wrapper.style.userSelect = 'text';
                    const focusEditor = () => { try { this.editor.focus(); } catch(e){} };
                    wrapper.addEventListener('mousedown', focusEditor, {capture: true, passive: false});
                    wrapper.addEventListener('click', focusEditor, {capture: true, passive: false});
                    wrapper.addEventListener('pointerdown', focusEditor, {capture: true, passive: false});
                }

                // Force multiple refreshes to ensure layout renders properly
                setTimeout(() => this.editor.refresh(), 50);
                setTimeout(() => this.editor.refresh(), 300);
                setTimeout(() => this.editor.refresh(), 800);

                // Handle changes and sync back to WPCFTO
                this.editor.on('change', cm => {
                    const newValue = cm.getValue();
                    this.value = newValue;

                    const mode = (fieldObj && fieldObj.mode) ? String(fieldObj.mode).toLowerCase() : 'htmlmixed';
                    let output = newValue;
                    if (mode.indexOf('json') !== -1) {
                        try { output = JSON.stringify(JSON.parse(newValue)); } catch(_) { output = newValue; }
                    }

                    textarea.value = output;
                    const event = new Event('input', { bubbles: true });
                    textarea.dispatchEvent(event);

                    this.$emit('wpcfto-get-value', output);

                    // Re-run global required validation if present
                    try { if (typeof this.validateField === 'function') this.validateField(); } catch(e){}
                });
            }
        },
        beforeDestroy() {
          if (this._origConsoleError) { try { console.error = this._origConsoleError; } catch(e){} }
          if (this._origConsoleWarn)  { try { console.warn  = this._origConsoleWarn; }  catch(e){} }
          this._origConsoleError = null;
          this._origConsoleWarn  = null;
          if (this.editor) {
              this.editor.off('change');
              this.editor.toTextArea();
              this.editor = null;
          }
          if (this.domObserver) {
              this.domObserver.disconnect();
              this.domObserver = null;
          }
          if (this.resizeObserver) {
              this.resizeObserver.disconnect();
              this.resizeObserver = null;
          }
        },
        computed: {
          readableMode() {
              return this.languageLabels[this.currentMode] || this.currentMode;
          }
        },
        watch: {
          currentMode(newMode) {
              if (this.editor) {
                  this.editor.setOption('mode', newMode);
              }
          },
          field_value(newVal) {
              if (newVal !== this.value && this.editor) {
                  this.editor.setValue(newVal);
                  this.value = newVal;
              }
          }
        }
    });
})();