(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 :id="field_id" :name="field_name"></textarea>
                </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'
              }
          };
        },
        mounted() {
          this.$nextTick(() => {
              this.$nextTick(() => {
                  const selector = '#' + this.field_id;
                  this.waitForElementVisible(selector, () => {
                      const fieldObj = window.eval(this.fields);
                      let rawValue = fieldObj.value || '';
                      this.value = this.formatCode(rawValue, fieldObj.mode);
      
                      this.initCodeMirror(fieldObj);
                      this.refreshOnTabActivation();
                      this.addMutationObserver();
                      this.initResizeObserver(); // 👈 Add this
                  });
              });
          });
        },
        methods: {
            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;

                this.editor = CodeMirror.fromTextArea(textarea, {
                    mode: fieldObj?.mode || 'htmlmixed',
                    theme: fieldObj?.theme || 'default',
                    lineNumbers: true,
                    lineWrapping: true,
                    tabSize: 2,
                    autoCloseBrackets: true,
                    matchBrackets: true
                });

                this.editor.setValue(this.value);

                // 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;

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

                        let minified = newValue;

                        if (normalizedMode.includes('javascript') || normalizedMode.includes('json')) {
                            const parsed = JSON.parse(newValue);
                            minified = JSON.stringify(parsed);
                        }

                        fieldObj.value = minified;
                        textarea.value = minified;

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

                    } catch (e) {
                        fieldObj.value = newValue;
                        textarea.value = newValue;

                        const event = new Event('input', { bubbles: true });
                        textarea.dispatchEvent(event);
                    }
                });
            }
        },
        beforeDestroy() {
          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;
              }
          }
        }
    });
})();