/* PrimeLink Admin JS (REST-only)
 * Version: 1.0.0
 */

(function($){
  'use strict';

  // Global adminUrl fallback to avoid ReferenceError in older code paths.
  // Prefer mtPrimeLink.adminUrl if available, otherwise derive from ajaxurl or location.
  if (typeof window.adminUrl === 'undefined') {
    if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) {
      window.adminUrl = mtPrimeLink.adminUrl;
    } else if (typeof ajaxurl !== 'undefined') {
      window.adminUrl = ajaxurl.replace(/admin-ajax\.php.*/, '');
    } else {
      window.adminUrl = window.location.origin + '/wp-admin/';
    }
  }

  function escapeHtml(s){
    return String(s).replace(/[&<>"']/g, m =>
      ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m])
    );
  }

  // Console wrapper - only log if WP_DEBUG is enabled (Envato compliance)
  const debugConsole = {
    error: function() {
      if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.wpDebug) {
        console.error.apply(console, arguments);
      }
    },
    warn: function() {
      if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.wpDebug) {
        console.warn.apply(console, arguments);
      }
    },
    log: function() {
      if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.wpDebug) {
        console.log.apply(console, arguments);
      }
    },
    info: function() {
      if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.wpDebug) {
        console.info.apply(console, arguments);
      }
    },
    debug: function() {
      if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.wpDebug) {
        console.debug.apply(console, arguments);
      }
    }
  };

  // ============================================
  // Toast Notifications System
  // ============================================
  
  /**
   * Initialize toast container if it doesn't exist
   */
  function initToastContainer() {
    if ($('#mt_primelink-toast-container').length === 0) {
      $('body').append('<div id="mt_primelink-toast-container" class="mt_primelink-toast-container"></div>');
    }
  }

  /**
   * Show a toast notification
   * @param {string} type - 'success', 'error', 'warning', 'info'
   * @param {string} message - Message to display
   * @param {string} title - Optional title
   * @param {number} duration - Auto-dismiss duration in ms (0 = no auto-dismiss, default 5000)
   */
  function showToast(type, message, title, duration) {
    initToastContainer();
    
    const container = $('#mt_primelink-toast-container');
    const toastId = 'toast-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
    const autoDismiss = duration !== undefined ? duration : 5000;
    
    // Icon mapping
    const icons = {
      success: '✓',
      error: '✕',
      warning: '⚠',
      info: 'ℹ'
    };
    
    const icon = icons[type] || icons.info;
    
    // Create toast HTML
    const toastHtml = `
      <div class="mt_primelink-toast mt_primelink-toast-${type}" id="${toastId}" data-toast-id="${toastId}">
        <div class="mt_primelink-toast-icon">${escapeHtml(icon)}</div>
        <div class="mt_primelink-toast-content">
          ${title ? `<div class="mt_primelink-toast-title">${escapeHtml(title)}</div>` : ''}
          <div class="mt_primelink-toast-message">${escapeHtml(message)}</div>
        </div>
        <button type="button" class="mt_primelink-toast-close" aria-label="Close">×</button>
        ${autoDismiss > 0 ? '<div class="mt_primelink-toast-progress"><div class="mt_primelink-toast-progress-bar"></div></div>' : ''}
      </div>
    `;
    
    const $toast = $(toastHtml);
    container.append($toast);
    
    // Trigger animation
    setTimeout(() => {
      $toast.addClass('mt_primelink-toast-visible');
    }, 10);
    
    // Auto-dismiss progress bar
    if (autoDismiss > 0) {
      const $progressBar = $toast.find('.mt_primelink-toast-progress-bar');
      $progressBar.css('width', '100%');
      $progressBar.css('transition', `width ${autoDismiss}ms linear`);
      
      setTimeout(() => {
        $progressBar.css('width', '0%');
      }, 50);
      
      const dismissTimer = setTimeout(() => {
        dismissToast(toastId);
      }, autoDismiss);
      
      $toast.data('dismiss-timer', dismissTimer);
    }
    
    // Close button handler
    $toast.find('.mt_primelink-toast-close').on('click', function() {
      dismissToast(toastId);
    });
    
    // Pause auto-dismiss on hover
    if (autoDismiss > 0) {
      $toast.on('mouseenter', function() {
        const timer = $(this).data('dismiss-timer');
        if (timer) {
          clearTimeout(timer);
          $(this).data('dismiss-timer', null);
          const $progressBar = $(this).find('.mt_primelink-toast-progress-bar');
          const remaining = autoDismiss - (Date.now() - ($(this).data('created') || Date.now()));
          if (remaining > 0) {
            $progressBar.css('transition', 'none');
            const currentWidth = parseFloat($progressBar.css('width')) / parseFloat($progressBar.parent().css('width')) * 100;
            $progressBar.css('width', currentWidth + '%');
          }
        }
      });
      
      $toast.on('mouseleave', function() {
        const remaining = autoDismiss - (Date.now() - ($(this).data('created') || Date.now()));
        if (remaining > 0) {
          const $progressBar = $(this).find('.mt_primelink-toast-progress-bar');
          $progressBar.css('transition', `width ${remaining}ms linear`);
          setTimeout(() => {
            $progressBar.css('width', '0%');
          }, 10);
          
          const dismissTimer = setTimeout(() => {
            dismissToast(toastId);
          }, remaining);
          
          $(this).data('dismiss-timer', dismissTimer);
        }
      });
      
      $toast.data('created', Date.now());
    }
    
    return toastId;
  }
  
  /**
   * Dismiss a toast notification
   * @param {string} toastId - Toast ID to dismiss
   */
  function dismissToast(toastId) {
    const $toast = $('#' + toastId);
    if ($toast.length === 0) return;
    
    // Clear dismiss timer if exists
    const timer = $toast.data('dismiss-timer');
    if (timer) {
      clearTimeout(timer);
    }
    
    // Add exit animation class
    $toast.addClass('mt_primelink-toast-exit');
    
    // Remove after animation
    setTimeout(() => {
      $toast.remove();
    }, 300);
  }
  
  /**
   * Helper functions for different toast types
   */
  window.mtPrimeLinkToast = {
    success: function(message, title, duration) {
      return showToast('success', message, title, duration);
    },
    error: function(message, title, duration) {
      return showToast('error', message, title, duration);
    },
    warning: function(message, title, duration) {
      return showToast('warning', message, title, duration);
    },
    info: function(message, title, duration) {
      return showToast('info', message, title, duration);
    },
    dismiss: function(toastId) {
      dismissToast(toastId);
    }
  };

  /**
   * Helper function to show toast or fallback to alert
   * Use this to gradually replace alert() calls
   */
  function showToastOrAlert(type, message, title, duration) {
    if (window.mtPrimeLinkToast && typeof window.mtPrimeLinkToast[type] === 'function') {
      return window.mtPrimeLinkToast[type](message, title, duration);
    } else {
      // Fallback to alert
      if (title) {
        alert(title + ': ' + message);
      } else {
        alert(message);
      }
      return null;
    }
  }

  // ============================================
  // Keyboard Shortcuts System
  // ============================================
  
  /**
   * Initialize keyboard shortcuts based on current page context
   */
  function initKeyboardShortcuts() {
    // Don't interfere with inputs, textareas, or contenteditable elements
    $(document).on('keydown', function(e) {
      // Ignore if user is typing in an input, textarea, or contenteditable
      const $target = $(e.target);
      const isInput = $target.is('input, textarea, [contenteditable="true"]');
      const isSelect = $target.is('select');
      
      // Allow shortcuts if Ctrl/Cmd is pressed (for save shortcuts) or Esc
      if (isInput && e.key !== 'Escape' && !(e.ctrlKey || e.metaKey)) {
        return;
      }
      
      // Check for specific shortcuts
      const isCtrlOrCmd = e.ctrlKey || e.metaKey;
      
      // Ctrl/Cmd + S - Save
      if (isCtrlOrCmd && e.key === 's' || e.key === 'S') {
        e.preventDefault();
        handleSaveShortcut();
        return false;
      }
      
      // Ctrl/Cmd + N - New Import (only on dashboard/manage pages)
      if (isCtrlOrCmd && (e.key === 'n' || e.key === 'N')) {
        e.preventDefault();
        handleNewImportShortcut();
        return false;
      }
      
      // Esc - Close modal/dialog
      if (e.key === 'Escape') {
        handleEscapeShortcut();
        return false;
      }
    });
    
    // Show keyboard shortcuts hint (optional, can be toggled)
    if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.showShortcutsHint !== false) {
      setTimeout(function() {
        showShortcutsHint();
      }, 2000);
    }
  }
  
  /**
   * Handle Ctrl/Cmd + S - Save current step/settings
   */
  function handleSaveShortcut() {
    // Check for Settings page save button
    const $settingsSave = $('#mt_primelink-save-settings');
    if ($settingsSave.length && $settingsSave.is(':visible') && !$settingsSave.prop('disabled')) {
      $settingsSave.trigger('click');
      showShortcutFeedback('Settings saved', 'Ctrl+S');
      return;
    }
    
    // Check for template save button
    const $templateSave = $('#mt_primelink-save-template');
    if ($templateSave.length && $templateSave.is(':visible') && !$templateSave.prop('disabled')) {
      $templateSave.trigger('click');
      showShortcutFeedback('Template saved', 'Ctrl+S');
      return;
    }
    
    // Check for Next/Finish button in wizard
    const $next = $('#mt-primelink-next');
    if ($next.length && $next.is(':visible') && !$next.prop('disabled')) {
      // Check if it's the last step (finish)
      const $finish = $('#mt-primelink-finish');
      if ($finish.length && $finish.is(':visible')) {
        // Don't auto-finish, just show hint
        showShortcutFeedback('Press Ctrl+S to save, or click Finish to complete', 'Ctrl+S');
      } else {
        $next.trigger('click');
        showShortcutFeedback('Step saved', 'Ctrl+S');
      }
      return;
    }
    
    // No save action available
    showShortcutFeedback('No save action available on this page', 'Ctrl+S');
  }
  
  /**
   * Handle Ctrl/Cmd + N - New Import
   */
  function handleNewImportShortcut() {
    // Check if we're on a page where New Import makes sense
    const currentPage = window.location.href;
    
    if (currentPage.includes('mt-primelink') && 
        !currentPage.includes('mt-primelink-new-import') && 
        !currentPage.includes('mt-primelink-new-export')) {
      
      // Check if New Import button exists on current page
      const $newImportBtn = $('a[href*="mt-primelink-new-import"]').first();
      if ($newImportBtn.length) {
        window.location.href = $newImportBtn.attr('href');
        return;
      }
      
      // Otherwise navigate to new import select page
      if (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) {
        window.location.href = mtPrimeLink.adminUrl + 'admin.php?page=mt-primelink-new-import';
      } else {
        // Fallback
        const adminBase = window.location.origin + '/wp-admin/';
        window.location.href = adminBase + 'admin.php?page=mt-primelink-new-import';
      }
    }
  }
  
  /**
   * Handle Esc - Close modal/dialog
   */
  function handleEscapeShortcut() {
    // Check for visible modals
    const $modal = $('.mt-primelink-modal:visible, .mt-primelink-modal__overlay:visible').closest('.mt-primelink-modal');
    if ($modal.length) {
      $modal.find('.mt-primelink-modal__close').first().trigger('click');
      return;
    }
    
    // Check for map editor (if visible)
    const $mapEditor = $('.mt_primelink-map-editor:visible');
    if ($mapEditor.length) {
      $mapEditor.find('.mt_primelink-map-editor-close').first().trigger('click');
      return;
    }
    
    // Check for any other closeable overlays
    const $overlay = $('.mt-primelink-wizard-overlay:visible');
    if ($overlay.length) {
      // Don't close wizard automatically, just show hint
      return;
    }
  }
  
  /**
   * Show visual feedback for keyboard shortcut
   */
  function showShortcutFeedback(message, shortcut) {
    if (window.mtPrimeLinkToast) {
      window.mtPrimeLinkToast.info(message, 'Keyboard Shortcut: ' + shortcut, 2000);
    }
  }
  
  /**
   * Show keyboard shortcuts hint (one-time, on page load)
   */
  function showShortcutsHint() {
    // Only show if user hasn't dismissed it before
    const dismissed = localStorage.getItem('mt_primelink_shortcuts_dismissed');
    if (dismissed === 'true') {
      return;
    }
    
    // Determine which shortcuts are available on this page
    const shortcuts = [];
    
    if ($('#mt_primelink-save-settings').length || $('#mt_primelink-save-template').length || $('#mt-primelink-next').length) {
      shortcuts.push('Ctrl+S - Save');
    }
    
    if (window.location.href.includes('mt-primelink') && !window.location.href.includes('mt-primelink-new-import')) {
      shortcuts.push('Ctrl+N - New Import');
    }
    
    if ($('.mt-primelink-modal:visible, .mt_primelink-map-editor:visible').length) {
      shortcuts.push('Esc - Close');
    }
    
    if (shortcuts.length === 0) {
      return;
    }
    
    // Show hint toast (can be dismissed)
    if (window.mtPrimeLinkToast) {
      const toastId = window.mtPrimeLinkToast.info(
        'Keyboard shortcuts: ' + shortcuts.join(', '),
        'Keyboard Shortcuts',
        8000
      );
      
      // Add dismiss button to localStorage
      setTimeout(function() {
        const $toast = $('[data-toast-id="' + toastId + '"]');
        if ($toast.length) {
          const $dismissBtn = $('<button type="button" class="button-link" style="margin-left:10px;font-size:11px;">Don\'t show again</button>');
          $dismissBtn.on('click', function(e) {
            e.stopPropagation();
            localStorage.setItem('mt_primelink_shortcuts_dismissed', 'true');
            if (window.mtPrimeLinkToast && window.mtPrimeLinkToast.dismiss) {
              window.mtPrimeLinkToast.dismiss(toastId);
            }
          });
          $toast.find('.mt_primelink-toast-content').append($dismissBtn);
        }
      }, 100);
    }
  }
  
  /**
   * Show keyboard shortcuts modal
   */
  function showShortcutsModal() {
    // Check if modal already exists
    let $modal = $('#mt-primelink-shortcuts-modal');
    
    if ($modal.length === 0) {
      // Detect if Mac (for Cmd vs Ctrl)
      const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
      const modifierKey = isMac ? 'Cmd' : 'Ctrl';
      
      // Create modal HTML
      const modalHtml = `
        <div id="mt-primelink-shortcuts-modal" class="mt_primelink-shortcuts-modal">
          <div class="mt_primelink-shortcuts-modal__content">
            <div class="mt_primelink-shortcuts-modal__header">
              <h2>Keyboard Shortcuts</h2>
              <button type="button" class="mt_primelink-shortcuts-modal__close" aria-label="Close">&times;</button>
            </div>
            <div class="mt_primelink-shortcuts-modal__body">
              <ul class="mt_primelink-shortcuts-list">
                <li class="mt_primelink-shortcuts-list__item">
                  <div class="mt_primelink-shortcuts-list__description">
                    <strong>Save</strong>
                    <span>Save settings, template, or current step</span>
                  </div>
                  <div class="mt_primelink-shortcuts-list__keys">
                    <kbd>${modifierKey}</kbd><span class="mt_primelink-shortcuts-list__separator">+</span><kbd>S</kbd>
                  </div>
                </li>
                <li class="mt_primelink-shortcuts-list__item">
                  <div class="mt_primelink-shortcuts-list__description">
                    <strong>New Import</strong>
                    <span>Navigate to New Import page</span>
                  </div>
                  <div class="mt_primelink-shortcuts-list__keys">
                    <kbd>${modifierKey}</kbd><span class="mt_primelink-shortcuts-list__separator">+</span><kbd>N</kbd>
                  </div>
                </li>
                <li class="mt_primelink-shortcuts-list__item">
                  <div class="mt_primelink-shortcuts-list__description">
                    <strong>Close Modal</strong>
                    <span>Close any open modal or dialog</span>
                  </div>
                  <div class="mt_primelink-shortcuts-list__keys">
                    <kbd>Esc</kbd>
                  </div>
                </li>
              </ul>
              <p style="margin-top: 24px; padding-top: 24px; border-top: 1px solid #f0f0f1; font-size: 12px; color: #646970;">
                ${isMac ? 'Note: Use Cmd key on Mac' : 'Note: Use Ctrl key on Windows/Linux'}
              </p>
            </div>
          </div>
        </div>
      `;
      
      $modal = $(modalHtml);
      $('body').append($modal);
      
      // Close handlers
      $modal.find('.mt_primelink-shortcuts-modal__close').on('click', function() {
        hideShortcutsModal();
      });
      
      $modal.on('click', function(e) {
        if ($(e.target).hasClass('mt_primelink-shortcuts-modal')) {
          hideShortcutsModal();
        }
      });
      
      // Close on Esc
      $(document).on('keydown.shortcuts-modal', function(e) {
        if (e.key === 'Escape' && $modal.hasClass('is-active')) {
          hideShortcutsModal();
        }
      });
    }
    
    // Show modal
    setTimeout(function() {
      $modal.addClass('is-active');
    }, 10);
  }
  
  /**
   * Hide keyboard shortcuts modal
   */
  function hideShortcutsModal() {
    const $modal = $('#mt-primelink-shortcuts-modal');
    if ($modal.length) {
      $modal.removeClass('is-active');
      $(document).off('keydown.shortcuts-modal');
    }
  }
  
  // Handle shortcuts info button click
  $(document).on('click', '#mt-primelink-show-shortcuts', function(e) {
    e.preventDefault();
    showShortcutsModal();
  });
  
  // Initialize keyboard shortcuts on document ready
  $(document).ready(function() {
    initKeyboardShortcuts();
  });

  function currentImportId(){
    const url = new URL(window.location.href);
    let id = parseInt(url.searchParams.get('import_id') || '0', 10);
    if(!id){
      const $step = $('[data-import-id]').first();
      id = parseInt($step.data('import-id') || '0', 10);
    }
    return id || 0;
  }

  function setImportIdEverywhere(importId){
    const url = new URL(window.location.href);
    url.searchParams.set('import_id', importId);
    history.replaceState({}, '', url.toString());

    $('#mt-primelink-next, #mt-primelink-finish').data('import-id', importId);
  }

  async function restPost(path, body, isFormData=false){
    if (typeof mtPrimeLink === 'undefined') {
      throw { status: 0, json: { message: 'mtPrimeLink is not defined. Make sure the script is properly enqueued.' } };
    }
    
    if (!mtPrimeLink.restUrl) {
      throw { status: 0, json: { message: 'REST API URL is not configured. Please refresh the page.' } };
    }
    
    const opts = {
      method: 'POST',
      headers: {
        'X-WP-Nonce': mtPrimeLink.restNonce
      }
    };
    if(isFormData){
      // For FormData, don't set Content-Type header (browser will set it with boundary)
      // But keep X-WP-Nonce header
      opts.body = body;
    } else {
      opts.headers['Content-Type'] = 'application/json';
      opts.body = JSON.stringify(body || {});
    }

    // Ensure path starts with / if restUrl doesn't end with /
    const baseUrl = mtPrimeLink.restUrl.endsWith('/') ? mtPrimeLink.restUrl.slice(0, -1) : mtPrimeLink.restUrl;
    const requestPath = path.startsWith('/') ? path : '/' + path;
    const fullUrl = baseUrl + requestPath;

    try {
      const r = await fetch(fullUrl, opts);
      let j = {};
      try {
        j = await r.json();
      } catch (jsonError) {
        // If JSON parsing fails, try to get text
        const text = await r.text();
        j = { message: text || 'Invalid JSON response from server', raw: text };
      }
      
      if(!r.ok) {
        const error = {status:r.status, json:j};
        debugConsole.error('REST API Error:', path, error);
        throw error;
      }
      return j;
    } catch (e) {
      // If it's already our error object, re-throw it
      if (e.status !== undefined) {
        throw e;
      }
      // Otherwise, wrap it
      debugConsole.error('Network error:', e);
      throw { status: 0, json: { message: e.message || 'Network error or server unavailable' } };
    }
  }

  async function restGet(path){
    try{
      const r = await fetch(mtPrimeLink.restUrl + path, {
        method: 'GET',
        headers: { 'X-WP-Nonce': mtPrimeLink.restNonce }
      });
      const j = await r.json().catch(()=> ({}));
      if(!r.ok){
        const error = {status:r.status, json:j};
        debugConsole.error('REST API GET Error:', path, error);
        throw error;
      }
      return j;
    }catch(e){
      if (e.status !== undefined) {
        throw e;
      }
      debugConsole.error('REST API GET Network error:', path, e);
      throw { status: 0, json: { message: e.message || 'Network error or server unavailable' } };
    }
  }

  function getStep(){
    return parseInt($('#mt-primelink-step').val(), 10) || 1;
  }

  function parseStepFromHref(href){
    try{
      const url = new URL(href, window.location.href);
      return parseInt(url.searchParams.get('step') || '0', 10) || 1;
    }catch(e){
      return getStep();
    }
  }

  // Full-page load navigation (no SPA)
  async function loadWizardStep(url){
    window.location.href = url;
  }

  function getStepData(){
    const data = {};
    $('.mt-primelink-wizard__body').find('input, select, textarea').each(function(){
      const $el = $(this);
      const name = $el.attr('name');
      if(!name) return;

      if($el.is(':radio')){
        if($el.is(':checked')) data[name] = $el.val();
      } else if($el.is(':checkbox')){
        data[name] = $el.is(':checked') ? 1 : 0;
      } else {
        data[name] = $el.val();
      }
    });
    return data;
  }

  // -----------------------------
  // Wizard Next
  // -----------------------------
  // Block clicking on steps ahead of current
  $(document).on('click', '.mt-primelink-step', function(e){
    const targetStep = parseStepFromHref($(this).attr('href'));
    const currentStep = getStep();

    // Prevent jumping ahead or backwards: enforce linear flow
    if (targetStep !== currentStep) {
      e.preventDefault();
      return false;
    }
  });

  $(document).on('click', '#mt-primelink-next', async function(e){
    e.preventDefault();

    const nextStep = parseInt($(this).data('next'), 10);
    const step     = getStep();
    let importId   = parseInt($(this).data('import-id'), 10) || currentImportId();

    // Step-specific validation before saving
    if (step === 2) {
      // Require a source file/configuration before moving on
      const source = $('input[name="source"]:checked').val();
      let valid = false;

      if (source === 'desktop') {
        // Desktop: must have uploaded file (path stored in draft step 2 or in hidden state)
        // Quick client-side check: look for any saved file info in the dropzone hint element
        // Fallback: block and show tooltip if not yet handled by AJAX.
        const hasFile = !!$('#mt-primelink-dropzone').data('file') || !!$('#mt-primelink-dropzone').data('path');
        valid = hasFile;
        if (!valid) {
          showNextTooltip('Please upload a file before continuing.');
        }
      } else if (source === 'existing') {
        const existingPath = $('#mt-primelink-existing-path').val();
        valid = !!(existingPath && String(existingPath).trim());
        if (!valid) {
          showNextTooltip('Please enter an existing file path before continuing.');
        }
      } else if (source === 'url') {
        const url = $('#mt-primelink-url').val();
        valid = !!(url && String(url).trim());
        if (!valid) {
          showNextTooltip('Please enter a URL before continuing.');
        }
      } else if (source === 'ftp') {
        // For FTP we at least require host; file path can be empty for connection test
        const ftpHost = $('#mt-primelink-ftp-host').val();
        valid = !!(ftpHost && String(ftpHost).trim());
        if (!valid) {
          showNextTooltip('Please configure FTP host (and optionally file path) before continuing.');
        }
      } else if (source === 'dropbox') {
        // Check both possible selectors (with underscore and with dash)
        const dropboxPath = $('#mt_primelink-dropbox-path').val() || $('#mt-primelink-dropbox-path').val();
        const hasPathValue = !!(dropboxPath && String(dropboxPath).trim());
        // Also check if file was already loaded successfully (shows "File loaded" message)
        const $result = $('#mt-primelink-source-result');
        const hasLoadedMessage = $result.length > 0 && ($result.html().indexOf('File loaded') !== -1 || $result.html().indexOf('File stored') !== -1);
        valid = hasPathValue || hasLoadedMessage;
        if (!valid) {
          showNextTooltip('Please enter a Dropbox path or link before continuing. Use Browse to select a file or enter a path/link manually.');
        }
      } else if (source === 'gdrive') {
        // Check both possible selectors (with underscore and with dash)
        const gdriveFile = $('#mt_primelink-gdrive-file').val() || $('#mt-primelink-gdrive-file').val();
        const hasFileValue = !!(gdriveFile && String(gdriveFile).trim());
        // Also check if file was already loaded successfully (shows "File loaded" message)
        const $result = $('#mt-primelink-source-result');
        const hasLoadedMessage = $result.length > 0 && ($result.html().indexOf('File loaded') !== -1 || $result.html().indexOf('File stored') !== -1);
        valid = hasFileValue || hasLoadedMessage;
        if (!valid) {
          showNextTooltip('Please enter a Google Drive file ID or link before continuing.');
        }
      } else if (source === 'onedrive') {
        // Check both possible selectors (with underscore and with dash)
        const onedriveFile = $('#mt_primelink-onedrive-file').val() || $('#mt-primelink-onedrive-file').val();
        const hasFileValue = !!(onedriveFile && String(onedriveFile).trim());
        // Also check if file was already loaded successfully (shows "Selected" message)
        const $onedriveFiles = $('#mt_primelink-onedrive-files');
        const hasSelectedMessage = $onedriveFiles.length > 0 && ($onedriveFiles.html().indexOf('Selected:') !== -1 || $onedriveFiles.html().indexOf('File loaded') !== -1);
        valid = hasFileValue || hasSelectedMessage;
        if (!valid) {
          showNextTooltip('Please select a OneDrive file before continuing. Use Browse to select a file or enter a file ID/link manually.');
        }
      } else if (source === 'platform') {
        // Platform imports: require at least one connector / configuration (server-side validated),
        // but on client we just ensure user selected this option – already true.
        valid = true;
      }

      if (!valid) {
        // Hard block moving to next step if no file/source set.
        return;
      }
    }

    // Step 5 critical field validation


    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      const payload = (step === 5) ? collectMappingState() : getStepData();
      if(step === 5){
        setSaveStatus('saving');
      }

      // Auto-set type for step 1 based on import_type from URL
      if(step === 1){
        const url = new URL(window.location.href);
        const importType = url.searchParams.get('import_type') || '';
        if(importType === 'orders'){
          payload.type = 'order';
        } else if(importType === 'customers'){
          payload.type = 'customer'; // Normalize: customers -> customer
        } else if(importType === 'coupons'){
          payload.type = 'coupon'; // Normalize: coupons -> coupon
        } else if(importType === 'products' || !importType){
          payload.type = 'product';
        } else {
          payload.type = importType;
        }
      }

      await restPost('/import/save-step', {
        import_id: importId,
        step: step,
        data: payload
      });
      if(step === 5){
        setSaveStatus('saved');
      }

      const url = new URL(window.location.href);
      url.searchParams.set('step', nextStep);
      url.searchParams.set('import_id', importId);
      window.location.href = url.toString();

    } catch(err){
      if(step === 5){
        setSaveStatus('error', err.json?.message || 'Save failed', true);
      }
      alert('REST error: ' + (err.json?.message || err.status || 'unknown'));
    }
  });

  // Back button (wizard) AJAX navigation
  $(document).on('click', '#mt-primelink-back', function(e){
    const prevStep = parseInt($(this).data('prev'), 10) || (getStep() - 1);
    if(prevStep > 0){
      const url = new URL($(this).attr('href'), window.location.href);
      url.searchParams.set('step', prevStep);
      // allow normal navigation; fallback set href explicitly
      window.location.href = url.toString();
    }
  });

  // -----------------------------
// Step 2 Source panels + upload (REST)
// -----------------------------
$(document).on('change','input[name="source"]', async function(){
  const val = $(this).val();
  $('.mt-panel').hide();
  $('.mt-panel[data-panel="'+val+'"]').show();
  
  // Platform connector specific handling
  if(val === 'platform'){
    await initPlatformConnector();
    // Load settings to get saved credentials (even if mt_primelinkSettings already exists but has no platform_connectors)
    try {
      if(!window.mt_primelinkSettings || !window.mt_primelinkSettings.platform_connectors || Array.isArray(window.mt_primelinkSettings.platform_connectors)){
        const settingsRes = await restGet('/settings?ts=' + Date.now());
        if(!window.mt_primelinkSettings) window.mt_primelinkSettings = {};
        let pc = settingsRes.platform_connectors || {};
        if (Array.isArray(pc)) {
          pc = {};
        }
        window.mt_primelinkSettings.platform_connectors = pc;
      }
    } catch(e){
      // Silent fail; user can still enter credentials manually
    }
  }
});

// Existing file helpers
$(document).on('click', '#mt_primelink-existing-browse', function(){
  const $input = $('#mt-primelink-existing-path');
  if(!$input.val()){
    $input.val('/wp-content/uploads/primelink/');
  }
  $input.focus();
  try{
    const el = $input.get(0);
    el.setSelectionRange($input.val().length, $input.val().length);
  }catch(e){}
});

$(document).on('change', '#mt_primelink-existing-recent', function(){
  const val = $(this).val();
  if(!val) return;
  const $input = $('#mt-primelink-existing-path');
  $input.val(val).focus();
  $input.trigger('blur');
});

$(document).on('click', '#mt_primelink-existing-refresh', function(){
  fetchExistingUploads();
});

  // Drag & Drop File Upload
  const dropzone = $('#mt-primelink-dropzone');
  const fileInput = $('#mt-primelink-upload-file');
  const dropzoneContent = $('.mt-primelink-dropzone-content');
  const dropzoneProgress = $('#mt-primelink-dropzone-progress');

  // Click on dropzone to trigger file input
  if (dropzone.length && fileInput.length) {
    dropzone.on('click', function(e) {
      // Don't trigger if clicking on the browse link (it has its own handler)
      if (!$(e.target).closest('.mt-primelink-dropzone-link').length) {
        fileInput.trigger('click');
      }
    });

    // Prevent default drag behaviors
    $(document).on('dragenter', '#mt-primelink-dropzone', function(e) {
      e.preventDefault();
      e.stopPropagation();
    });

    $(document).on('dragover', '#mt-primelink-dropzone', function(e) {
      e.preventDefault();
      e.stopPropagation();
      $(this).addClass('drag-over');
    });

    $(document).on('dragleave', '#mt-primelink-dropzone', function(e) {
      e.preventDefault();
      e.stopPropagation();
      // Only remove class if we're leaving the dropzone, not entering a child element
      if (!$(this).is(e.currentTarget)) {
        return;
      }
      const rect = this.getBoundingClientRect();
      const x = e.clientX;
      const y = e.clientY;
      if (x < rect.left || x >= rect.right || y < rect.top || y >= rect.bottom) {
        $(this).removeClass('drag-over');
      }
    });

    $(document).on('drop', '#mt-primelink-dropzone', function(e) {
      e.preventDefault();
      e.stopPropagation();
      $(this).removeClass('drag-over');

      const files = e.originalEvent.dataTransfer.files;
      if (files.length > 0) {
        handleFileSelection(files[0]);
      }
    });

    // Helper function to validate and handle file selection
    function handleFileSelection(file) {
      if (!file) return;

      // Validate file type
      const validExtensions = ['.csv', '.xml', '.xlsx', '.xls'];
      const fileName = file.name.toLowerCase();
      const isValidFile = validExtensions.some(ext => fileName.endsWith(ext));

      if (!isValidFile) {
        if (window.mtPrimeLinkToast) {
          window.mtPrimeLinkToast.error('Invalid file type. Please select a CSV, XML, XLSX, or XLS file.', 'Invalid File');
        } else {
          alert('Invalid file type. Please select a CSV, XML, XLSX, or XLS file.');
        }
        return;
      }

      // Create a DataTransfer object to set the file in the input
      const dataTransfer = new DataTransfer();
      dataTransfer.items.add(file);
      
      // Set the file in the input (this works in modern browsers)
      fileInput[0].files = dataTransfer.files;
      
      // Trigger change event to use existing AJAX upload logic
      fileInput.trigger('change');
    }
  }

  $(document).on('change', '#mt-primelink-upload-file', function(){
    const file = this.files[0];
    if(!file) return;

    let importId = currentImportId();
    const self = this;
    
    // Create draft if needed (using REST API for this)
    (async function() {
      try {
        if(!importId || importId === 0){
          const draft = await restPost('/import/create-draft', {});
          if (!draft || !draft.import_id) {
            throw { status: 0, json: { message: 'Failed to create draft' } };
          }
          importId = draft.import_id;
          setImportIdEverywhere(importId);
        }

        // Use AJAX for file upload (works with FormData and $_FILES)
        const fd = new FormData();
        fd.append('action', 'mt_primelink_import_handle_source');
        fd.append('nonce', mtPrimeLink.ajaxNonce);
        fd.append('import_id', importId.toString());
        fd.append('source', 'desktop');
        fd.append('file', file);

        // Show progress in dropzone
        const dropzoneContent = $('.mt-primelink-dropzone-content');
        const dropzoneProgress = $('#mt-primelink-dropzone-progress');
        const progressText = $('#mt-primelink-dropzone-progress-text');
        
        if (dropzoneContent.length && dropzoneProgress.length) {
          dropzoneContent.hide();
          dropzoneProgress.show();
          progressText.text('Uploading... 0%');
          $('#mt-primelink-dropzone-progress-fill').css('width', '0%');
        }

        $('#mt-primelink-source-result').show().text('Uploading...');

        $.ajax({
          url: mtPrimeLink.ajaxUrl,
          type: 'POST',
          data: fd,
          processData: false,
          contentType: false,
          xhr: function() {
            const xhr = new window.XMLHttpRequest();
            // Track upload progress
            xhr.upload.addEventListener('progress', function(e) {
              if (e.lengthComputable) {
                const percentComplete = Math.round((e.loaded / e.total) * 100);
                const progressFill = $('#mt-primelink-dropzone-progress-fill');
                const progressText = $('#mt-primelink-dropzone-progress-text');
                if (progressFill.length) {
                  progressFill.css('width', percentComplete + '%');
                }
                if (progressText.length) {
                  progressText.text('Uploading... ' + percentComplete + '%');
                }
              }
            }, false);
            return xhr;
          },
          success: function(response) {
            
            // Reset dropzone UI
            const dropzoneContent = $('.mt-primelink-dropzone-content');
            const dropzoneProgress = $('#mt-primelink-dropzone-progress');
            if (dropzoneContent.length && dropzoneProgress.length) {
              dropzoneProgress.hide();
              dropzoneContent.show();
              $('#mt-primelink-dropzone-progress-fill').css('width', '0%');
            }
            
            if (response.success && response.data && response.data.file) {
              // Mark dropzone as having a valid file so step-2 validation passes
              $('#mt-primelink-dropzone')
                .data('file', response.data.file)
                .data('path', response.data.file.path || '')
                .data('type', response.data.file.type || '');

              $('#mt-primelink-source-result')
                .html('<strong>File stored:</strong> '+escapeHtml(response.data.file.original_name)+' ('+escapeHtml(response.data.file.type)+')');
            } else {
              const errorMsg = response.data?.message || 'Upload failed';
              $('#mt-primelink-source-result').show().text(errorMsg);
            }
          },
          error: function(xhr, status, error) {
            debugConsole.error('Upload error:', xhr, status, error);
            
            // Reset dropzone UI on error
            const dropzoneContent = $('.mt-primelink-dropzone-content');
            const dropzoneProgress = $('#mt-primelink-dropzone-progress');
            if (dropzoneContent.length && dropzoneProgress.length) {
              dropzoneProgress.hide();
              dropzoneContent.show();
              $('#mt-primelink-dropzone-progress-fill').css('width', '0%');
            }
            
            let errorMsg = 'Upload failed';
            try {
              const response = JSON.parse(xhr.responseText);
              errorMsg = response.data?.message || errorMsg;
            } catch(e) {}
            $('#mt-primelink-source-result').show().text(errorMsg);
          }
        });

      } catch(err){
        debugConsole.error('Draft creation error:', err);
        
        // Reset dropzone UI on error
        const dropzoneContent = $('.mt-primelink-dropzone-content');
        const dropzoneProgress = $('#mt-primelink-dropzone-progress');
        if (dropzoneContent.length && dropzoneProgress.length) {
          dropzoneProgress.hide();
          dropzoneContent.show();
          $('#mt-primelink-dropzone-progress-fill').css('width', '0%');
        }
        
        const errorMsg = err.json?.message || err.json?.error || 'Failed to create draft';
        $('#mt-primelink-source-result').show().text(errorMsg);
      }
    })();
  });

  $(document).on('blur', '#mt-primelink-url', async function(){
    const urlVal = $(this).val();
    if(!urlVal) return;

    let importId = currentImportId();
    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      $('#mt-primelink-source-result').show().text('Downloading...');

      // Use AJAX for URL (works better with POST data)
      $.ajax({
        url: mtPrimeLink.ajaxUrl,
        type: 'POST',
        data: {
          action: 'mt_primelink_import_handle_source',
          nonce: mtPrimeLink.ajaxNonce,
          import_id: importId,
          source: 'url',
          url: urlVal
        },
        success: function(response) {
          
          if(response.success && response.data && response.data.file){
            const file = response.data.file;
            if(file.original_name){
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File stored:</strong> '+escapeHtml(file.original_name)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else if(file.path){
              const fileName = file.path.split('/').pop() || file.path.split('\\').pop() || 'file';
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File stored:</strong> '+escapeHtml(fileName)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else {
              $('#mt-primelink-source-result').html('<span style="color:#0a0;">File stored successfully.</span>');
            }
          } else {
            const errorMsg = response.data?.message || 'Download failed';
            $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
          }
        },
        error: function(xhr, status, error) {
          debugConsole.error('URL download error:', xhr, status, error);
          let errorMsg = 'Download failed';
          try {
            const response = JSON.parse(xhr.responseText);
            if(response.data && response.data.message){
              errorMsg = response.data.message;
            }
          } catch(e){}
          $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
        }
      });

    } catch(err){
      $('#mt-primelink-source-result').show().text(err.json?.message || 'Download failed');
    }
  });

  $(document).on('blur', '#mt-primelink-existing-path', async function(){
    const pathVal = $(this).val();
    if(!pathVal) return;

    let importId = currentImportId();
    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      $('#mt-primelink-source-result').show().text('Validating file...');

      // Use AJAX for existing file (works better with POST data)
      $.ajax({
        url: mtPrimeLink.ajaxUrl,
        type: 'POST',
        data: {
          action: 'mt_primelink_import_handle_source',
          nonce: mtPrimeLink.ajaxNonce,
          import_id: importId,
          source: 'existing',
          path: pathVal
        },
        success: function(response) {
          
          if(response.success && response.data && response.data.file){
            const file = response.data.file;
            if(file.original_name){
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(file.original_name)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else if(file.path){
              const fileName = file.path.split('/').pop() || file.path.split('\\').pop() || 'file';
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(fileName)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else {
              $('#mt-primelink-source-result').html('<span style="color:#0a0;">File loaded successfully.</span>');
            }
            try {
              addExistingRecent(pathVal);
            } catch(e){}
          } else {
            const errorMsg = response.data?.message || 'Invalid file';
            $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
          }
        },
        error: function(xhr, status, error) {
          debugConsole.error('Existing file error:', xhr, status, error);
          let errorMsg = 'Invalid file';
          try {
            const response = JSON.parse(xhr.responseText);
            if(response.data && response.data.message){
              errorMsg = response.data.message;
            }
          } catch(e){}
          $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
        }
      });

    } catch(err){
      $('#mt-primelink-source-result').show().text(err.json?.message || 'Invalid file');
    }
  });

  async function fetchDropboxFile(pathVal){
    if(!pathVal) return;
    let importId = currentImportId();
    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      $('#mt-primelink-source-result').show().text('Connecting to Dropbox...');

      // Use AJAX for Dropbox (works better with POST data)
      $.ajax({
        url: mtPrimeLink.ajaxUrl,
        type: 'POST',
        data: {
          action: 'mt_primelink_import_handle_source',
          nonce: mtPrimeLink.ajaxNonce,
          import_id: importId,
          source: 'dropbox',
          path: pathVal
        },
        success: function(response) {
          
          if(response.success && response.data && response.data.file){
            const file = response.data.file;
            if(file.original_name){
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(file.original_name)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else if(file.path){
              const fileName = file.path.split('/').pop() || file.path.split('\\').pop() || 'file';
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(fileName)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else {
              $('#mt-primelink-source-result').html('<span style="color:#0a0;">File loaded successfully.</span>');
            }
          } else {
            const errorMsg = response.data?.message || 'Dropbox download failed';
            $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
          }
        },
        error: function(xhr, status, error) {
          debugConsole.error('Dropbox fetch error:', xhr, status, error);
          let errorMsg = 'Dropbox download failed';
          try {
            const response = JSON.parse(xhr.responseText);
            if(response.data && response.data.message){
              errorMsg = response.data.message;
            }
          } catch(e){}
          $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
        }
      });

    } catch(err){
      $('#mt-primelink-source-result').show().text(err.json?.message || 'Dropbox download failed');
    }
  }

  $(document).on('click', '#mt_primelink-dropbox-fetch', function(){
    const val = $('#mt_primelink-dropbox-path').val();
    fetchDropboxFile(val);
  });

  $(document).on('blur', '#mt_primelink-dropbox-path', function(){
    const val = $(this).val();
    fetchDropboxFile(val);
  });

  async function listDropbox(pathVal){
    $('#mt_primelink-dropbox-files').html('<em>Listing files...</em>');
    
    // Use AJAX for Dropbox list (works better with POST data)
    $.ajax({
      url: mtPrimeLink.ajaxUrl,
      type: 'POST',
      data: {
        action: 'mt_primelink_dropbox_list',
        nonce: mtPrimeLink.ajaxNonce,
        path: pathVal || ''
      },
      success: function(response) {
        
        if(response.success && response.data && response.data.files){
          const files = response.data.files || [];
          if(!files.length){
            $('#mt_primelink-dropbox-files').html('<em>No files found in this folder.</em>');
            return;
          }
          let html = '<ul class="mt_primelink-dropbox-listing">';
          files.forEach(f => {
            html += '<li><a href="#" data-path="'+escapeHtml(f.path)+'">'+escapeHtml(f.name)+'</a> <small>'+escapeHtml(f.size_human||'')+'</small></li>';
          });
          html += '</ul>';
          $('#mt_primelink-dropbox-files').html(html);
        } else {
          const errorMsg = response.data?.message || 'Failed to list files';
          $('#mt_primelink-dropbox-files').html('<em>'+escapeHtml(errorMsg)+'</em>');
        }
      },
      error: function(xhr, status, error) {
        debugConsole.error('Dropbox list error:', xhr, status, error);
        let errorMsg = 'Failed to list files';
        try {
          const response = JSON.parse(xhr.responseText);
          if(response.data && response.data.message){
            errorMsg = response.data.message;
          }
        } catch(e){}
        $('#mt_primelink-dropbox-files').html('<em>'+escapeHtml(errorMsg)+'</em>');
      }
    });
  }

  $(document).on('click', '#mt_primelink-dropbox-list', function(){
    const val = $('#mt_primelink-dropbox-path').val();
    listDropbox(val);
  });

  $(document).on('click', '.mt_primelink-dropbox-listing a', function(e){
    e.preventDefault();
    const p = $(this).data('path');
    if(p){
      $('#mt_primelink-dropbox-path').val(p);
      fetchDropboxFile(p);
    }
  });

  async function testFtpConnection(){
    const protocol = $('#mt_primelink-ftp-protocol').val() || 'ftp';
    const host = $('#mt_primelink-ftp-host').val() || '';
    const portVal = $('#mt_primelink-ftp-port').val();
    const port = portVal ? parseInt(portVal, 10) : 0;
    const user = $('#mt_primelink-ftp-user').val() || '';
    const pass = $('#mt_primelink-ftp-pass').val() || '';
    const root = $('#mt_primelink-ftp-root').val() || '';
    const path = $('#mt_primelink-ftp-file').val() || '';
    const passive = $('#mt_primelink-ftp-passive').is(':checked');

    if(!host || !user || !pass){
      $('#mt-primelink-source-result').show().html('<span style="color:#f00;">Please fill host, username, and password to test connection.</span>');
      return;
    }

    let importId = currentImportId();
    
    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      $('#mt-primelink-source-result').show().text('Testing FTP connection...');

      $.ajax({
        url: mtPrimeLink.ajaxUrl,
        type: 'POST',
        data: {
          action: 'mt_primelink_import_handle_source',
          nonce: mtPrimeLink.ajaxNonce,
          import_id: importId,
          source: 'ftp',
          protocol: protocol,
          host: host,
          port: port,
          user: user,
          pass: pass,
          root: root,
          path: path || '',
          passive: passive ? 1 : 0,
          test_only: 1
        },
        success: function(response) {
          if(response.success){
            const message = response.data?.message || response.data?.data?.message || 'Connection successful!';
            $('#mt-primelink-source-result').html('<span style="color:#46b450;">✓ ' + escapeHtml(message) + '</span>');
          } else {
            const errorMsg = response.data?.message || response.message || 'Unknown error';
            $('#mt-primelink-source-result').html('<span style="color:#f00;">✗ Connection failed: ' + escapeHtml(errorMsg) + '</span>');
          }
        },
        error: function(xhr, status, error){
          debugConsole.error('FTP test error:', xhr, status, error);
          let errorMsg = 'FTP/SFTP connection test failed';
          try {
            const response = JSON.parse(xhr.responseText);
            errorMsg = response.data?.message || errorMsg;
          } catch(e) {}
          $('#mt-primelink-source-result').html('<span style="color:#f00;">✗ ' + escapeHtml(errorMsg) + '</span>');
        }
      });
    } catch(err){
      debugConsole.error('FTP test error:', err);
      const errorMsg = err.json?.message || err.json?.error || 'Failed to test connection';
      $('#mt-primelink-source-result').show().html('<span style="color:#f00;">✗ ' + escapeHtml(errorMsg) + '</span>');
    }
  }

  async function fetchFtpFile(){
    const protocol = $('#mt_primelink-ftp-protocol').val() || 'ftp';
    const host = $('#mt_primelink-ftp-host').val() || '';
    const portVal = $('#mt_primelink-ftp-port').val();
    const port = portVal ? parseInt(portVal, 10) : 0;
    const user = $('#mt_primelink-ftp-user').val() || '';
    const pass = $('#mt_primelink-ftp-pass').val() || '';
    const root = $('#mt_primelink-ftp-root').val() || '';
    const path = $('#mt_primelink-ftp-file').val() || '';
    const passive = $('#mt_primelink-ftp-passive').is(':checked');

    if(!host || !user || !pass){
      $('#mt-primelink-source-result').show().html('<span style="color:#f00;">Please fill host, username, and password.</span>');
      return;
    }

    if(!path){
      $('#mt-primelink-source-result').show().html('<span style="color:#f00;">Please specify file path to download.</span>');
      return;
    }

    let importId = currentImportId();
    
    // Create draft if needed (using REST API for this)
    (async function() {
      try {
        if(!importId){
          const draft = await restPost('/import/create-draft', {});
          importId = draft.import_id;
          setImportIdEverywhere(importId);
        }

        $('#mt-primelink-source-result').show().html('<span>Connecting to FTP/SFTP...</span>');

        // Use AJAX for FTP (works better with POST data)
        $.ajax({
          url: mtPrimeLink.ajaxUrl,
          type: 'POST',
          data: {
            action: 'mt_primelink_import_handle_source',
            nonce: mtPrimeLink.ajaxNonce,
            import_id: importId,
            source: 'ftp',
            protocol: protocol,
            host: host,
            port: port,
            user: user,
            pass: pass,
            root: root,
            path: path,
            passive: passive ? 1 : 0
          },
          success: function(response) {
            
            if(response.success && response.data && response.data.file){
              const file = response.data.file;
              if(file.original_name){
                $('#mt-primelink-source-result')
                  .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(file.original_name)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
              } else if(file.path){
                const fileName = file.path.split('/').pop() || file.path.split('\\').pop() || 'file';
                $('#mt-primelink-source-result')
                  .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(fileName)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
              } else {
                $('#mt-primelink-source-result').html('<span style="color:#0a0;">File loaded successfully.</span>');
              }
            } else {
              const errorMsg = response.data?.message || 'FTP/SFTP download failed';
              $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
            }
          },
          error: function(xhr, status, error) {
            debugConsole.error('FTP fetch error:', xhr, status, error);
            let errorMsg = 'FTP/SFTP download failed';
            try {
              const response = JSON.parse(xhr.responseText);
              errorMsg = response.data?.message || errorMsg;
            } catch(e) {}
            $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
          }
        });

      } catch(err){
        debugConsole.error('Draft creation error:', err);
        const errorMsg = err.json?.message || err.json?.error || 'Failed to create draft';
        $('#mt-primelink-source-result').show().html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
      }
    })();
  }

  $(document).on('click', '#mt_primelink-ftp-test', function(){
    testFtpConnection();
  });

  $(document).on('click', '#mt_primelink-ftp-fetch', function(){
    fetchFtpFile();
  });

  async function fetchGdriveFile(){
    if(typeof $ === 'undefined' || typeof jQuery === 'undefined'){
      alert('jQuery is not loaded. Please refresh the page.');
      return;
    }
    
    const $fileInput = $('#mt_primelink-gdrive-file');
    if($fileInput.length === 0){
      alert('Google Drive file input not found. Please refresh the page.');
      return;
    }
    
    const file = $fileInput.val();
    if(!file || !file.trim()){
      const $result = $('#mt-primelink-source-result');
      if($result.length){
        $result.show().text('Please add a Google Drive file ID or link.');
      } else {
        alert('Please add a Google Drive file ID or link.');
      }
      return;
    }
    let importId = currentImportId();
    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      const $result = $('#mt-primelink-source-result');
      if($result.length === 0){
        $('body').append('<div class="mt-panel glass" id="mt-primelink-source-result" style="display:block; margin-top:10px;"></div>');
      }
      $('#mt-primelink-source-result').show().text('Fetching from Google Drive...');

      // Use AJAX for Google Drive (works better with POST data)
      $.ajax({
        url: mtPrimeLink.ajaxUrl || ajaxurl,
        type: 'POST',
        timeout: 60000,
        data: {
          action: 'mt_primelink_import_handle_source',
          nonce: mtPrimeLink.ajaxNonce || '',
          import_id: importId,
          source: 'gdrive',
          path: file
        },
        success: function(response) {
          if(response && response.success && response.data && response.data.file){
            const fileData = response.data.file;
            if(fileData.original_name){
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(fileData.original_name)+' ('+escapeHtml(fileData.type || 'unknown')+')</span>');
            } else if(fileData.path){
              const fileName = fileData.path.split('/').pop() || fileData.path.split('\\').pop() || 'file';
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(fileName)+' ('+escapeHtml(fileData.type || 'unknown')+')</span>');
            } else {
              $('#mt-primelink-source-result').html('<span style="color:#0a0;">File loaded successfully.</span>');
            }
          } else {
            const errorMsg = (response && response.data && response.data.message) ? response.data.message : 'Google Drive download failed. Please check the file ID and ensure the file is public or OAuth is configured.';
            $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
          }
        },
        error: function(xhr, status, error) {
          let errorMsg = 'Google Drive download failed';
          try {
            if(xhr.responseText){
              const response = JSON.parse(xhr.responseText);
              if(response && response.data && response.data.message){
                errorMsg = response.data.message;
              } else if(response && response.message){
                errorMsg = response.message;
              }
            }
          } catch(e){
            if(status === 'timeout'){
              errorMsg = 'Request timed out. The file may be too large or the server is slow. Please try again.';
            } else if(xhr.status === 0){
              errorMsg = 'Network error. Please check your connection.';
            } else if(xhr.status === 403){
              errorMsg = 'Access denied. Please check your Google Drive API Key or OAuth settings.';
            } else if(xhr.status === 404){
              errorMsg = 'File not found. Please check the file ID and ensure the file is public (shared with "Anyone with the link").';
            } else {
              errorMsg = 'Error ' + (xhr.status || 'unknown') + ': ' + (error || status || 'Unknown error');
            }
          }
          $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
        },
        timeout: 60000
      });

    } catch(err){
      $('#mt-primelink-source-result').show().text(err.json?.message || 'Google Drive download failed');
    }
  }

  $(document).on('click', '#mt_primelink-gdrive-fetch', function(e){
    e.preventDefault();
    e.stopPropagation();
    if(typeof fetchGdriveFile === 'function'){
      fetchGdriveFile();
    } else {
      alert('fetchGdriveFile function not found. Please refresh the page.');
    }
  });

  // Also try direct binding on ready as fallback
  $(document).ready(function(){
    // Re-bind on panel show to ensure handler is attached
    $('input[name="source"][value="gdrive"]').on('change', function(){
      setTimeout(function(){
        $('#mt_primelink-gdrive-fetch').off('click.primelink').on('click.primelink', function(e){
          e.preventDefault();
          e.stopPropagation();
          fetchGdriveFile();
        });
      }, 100);
    });
    
    // Also bind directly
    $('#mt_primelink-gdrive-fetch').off('click.primelink').on('click.primelink', function(e){
      e.preventDefault();
      e.stopPropagation();
      fetchGdriveFile();
    });
  });

  async function listGdrive(folderId, query, pageToken){
    $('#mt_primelink-gdrive-files').html('<em>Loading files...</em>');
    $('#mt_primelink-gdrive-search').show();
    
    try {
      const response = await restPost('/gdrive/list', {
        folder_id: folderId || 'root',
        query: query || '',
        page_token: pageToken || ''
      });
      
      if(response.files && response.files.length > 0){
        let html = '<ul class="mt_primelink-gdrive-listing">';
        response.files.forEach(f => {
          html += '<li><a href="#" data-id="'+escapeHtml(f.id)+'" data-name="'+escapeHtml(f.name)+'">';
          html += escapeHtml(f.name);
          html += ' <small>('+escapeHtml(f.size_human||'')+')</small>';
          html += '</a></li>';
        });
        html += '</ul>';
        
        if(response.next_page_token){
          html += '<button type="button" class="button button-small" id="mt_primelink-gdrive-load-more" data-page-token="'+escapeHtml(response.next_page_token)+'">Load More</button>';
        }
        
        $('#mt_primelink-gdrive-files').html(html);
      } else {
        let msg = 'No files found. ';
        if(response.requires_oauth || (err && err.json && err.json.requires_oauth)){
          msg += 'OAuth authentication is required for browsing files. Please click "Authenticate with Google Drive" in Settings → Integrations.';
        } else {
          msg += 'Make sure your files are public (shared with "Anyone with the link") or authenticate with OAuth.';
        }
        $('#mt_primelink-gdrive-files').html('<em>' + escapeHtml(msg) + '</em>');
      }
    } catch(err){
      const errorMsg = err.json?.message || err.message || 'Failed to list Google Drive files';
      $('#mt_primelink-gdrive-files').html('<em style="color:#f00;">'+escapeHtml(errorMsg)+'</em>');
    }
  }

  // Google Picker API implementation
  let googlePickerLoaded = false;
  
  function loadGooglePicker(callback) {
    if (googlePickerLoaded && typeof google !== 'undefined' && typeof google.picker !== 'undefined') {
      callback();
      return;
    }
    
    // Check if script is already in DOM
    let existingScript = document.querySelector('script[src*="apis.google.com/js/api.js"]');
    if (existingScript) {
      // Script exists, wait for gapi to be available
      const maxAttempts = 50;
      let attempts = 0;
      const checkInterval = setInterval(function() {
        attempts++;
        if (typeof gapi !== 'undefined') {
          clearInterval(checkInterval);
          gapi.load('picker', {
            callback: function() {
              googlePickerLoaded = true;
              callback();
            },
            onerror: function(error) {
              debugConsole.error('Google Picker load error:', error);
              alert('Failed to load Google Picker API. Please make sure Google Picker API is enabled in Google Cloud Console.');
            }
          });
        } else if (attempts >= maxAttempts) {
          clearInterval(checkInterval);
          alert('Timeout loading Google API. Please refresh the page and try again.');
        }
      }, 100);
      return;
    }
    
    // Load Google API script
    const script = document.createElement('script');
    script.src = 'https://apis.google.com/js/api.js';
    script.async = true;
    script.defer = true;
    script.onload = function() {
      // Wait a bit for gapi to initialize
      setTimeout(function() {
        if (typeof gapi !== 'undefined') {
          gapi.load('picker', {
            callback: function() {
              googlePickerLoaded = true;
              callback();
            },
            onerror: function(error) {
              debugConsole.error('Google Picker load error:', error);
              alert('Failed to load Google Picker API. Please make sure:\n1. Google Picker API is enabled in Google Cloud Console\n2. Your Client ID has the correct permissions');
            }
          });
        } else {
          alert('Google API (gapi) is not available. Please check your internet connection and try again.');
        }
      }, 500);
    };
    script.onerror = function() {
      alert('Failed to load Google API script. Please check your internet connection.');
    };
    document.head.appendChild(script);
  }
  
  function openGooglePicker() {
    const clientId = mtPrimeLink?.gdrive?.clientId || '';
    const apiKey = mtPrimeLink?.gdrive?.apiKey || '';
    const accessToken = mtPrimeLink?.gdrive?.accessToken || '';
    
    // Debug logging removed for production
    
    // Google Picker requires Client ID
    if (!clientId) {
      alert('Google Drive Client ID is required for Google Picker.\n\nPlease:\n1. Go to Settings → Integrations\n2. Add your Client ID\n3. Click "Authenticate with Google Drive" to get access token\n4. Then try "Browse" again');
      return;
    }
    
    // If no access token, Google Picker will try to authenticate, but it's better to authenticate first
    if (!accessToken) {
      const proceed = confirm('No Google Drive access token found. Google Picker will try to authenticate, but it\'s recommended to authenticate first.\n\nWould you like to:\n- Click OK to try Google Picker (it will prompt for authentication)\n- Click Cancel to authenticate first in Settings → Integrations');
      if (!proceed) {
        return;
      }
    }
    
    loadGooglePicker(function() {
      // Debug logging removed for production
      createPicker(clientId, apiKey, accessToken);
    });
  }
  
  function createPicker(clientId, apiKey, accessToken) {
    try {
      if (typeof google === 'undefined' || typeof google.picker === 'undefined') {
        alert('Google Picker is not available. Please refresh the page and try again.');
        return;
      }
      
      const view = new google.picker.View(google.picker.ViewId.DOCS);
      view.setMimeTypes('text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/xml,application/xml,application/json');
      
      const pickerBuilder = new google.picker.PickerBuilder()
        .enableFeature(google.picker.Feature.NAV_HIDDEN)
        .addView(view)
        .setCallback(pickerCallback);
      
      // For Google Picker, we need OAuth token OR we need to authenticate first
      // If we have access token, use it
      if (accessToken) {
        pickerBuilder.setOAuthToken(accessToken);
        // Debug logging removed for production
      } else if (clientId) {
        // If no access token but we have Client ID, we need to authenticate first
        // Google Picker will handle OAuth flow automatically if we set the scope
        pickerBuilder.setAppId(clientId);
        // Request OAuth token with the required scope
        const scope = 'https://www.googleapis.com/auth/drive.readonly';
        pickerBuilder.setOAuthToken(null); // This will trigger OAuth flow
        // Debug logging removed for production
      }
      
      // API Key alone is not sufficient for Google Picker - it needs OAuth
      if (apiKey && !accessToken && !clientId) {
        alert('Google Picker requires OAuth authentication. Please configure Client ID in Settings → Integrations and authenticate with Google Drive.');
        return;
      }
      
      if (apiKey && !accessToken) {
        pickerBuilder.setDeveloperKey(apiKey);
      }
      
      const picker = pickerBuilder.build();
      picker.setVisible(true);
      // Debug logging removed for production
    } catch (error) {
      debugConsole.error('Error creating Google Picker:', error);
      alert('Failed to open Google Picker: ' + error.message + '\n\nPlease make sure:\n1. Google Picker API is enabled in Google Cloud Console\n2. Your Client ID is correct\n3. OAuth consent screen has drive.readonly scope added\n4. You are logged in to Google\n5. If app is in Testing mode, your email is added as Test user');
    }
  }
  
  function pickerCallback(data) {
    if (data[google.picker.Response.ACTION] === google.picker.Action.PICKED) {
      const docs = data[google.picker.Response.DOCUMENTS];
      if (docs && docs.length > 0) {
        const fileId = docs[0].id;
        const fileName = docs[0].name || '';
        $('#mt_primelink-gdrive-file').val(fileId);
        $('#mt_primelink-gdrive-files').html('<div style="padding:10px; background:#d4edda; border-left:4px solid #28a745; border-radius:4px;"><strong>Selected:</strong> ' + escapeHtml(fileName) + ' <small>(ID: ' + escapeHtml(fileId) + ')</small></div>');
        // Automatically fetch the file
        fetchGdriveFile();
      }
    } else if (data[google.picker.Response.ACTION] === google.picker.Action.CANCEL) {
      // User cancelled - do nothing
    }
  }

  $(document).on('click', '#mt_primelink-gdrive-list', function(e){
    e.preventDefault();
    e.stopPropagation();
    
    const clientId = mtPrimeLink?.gdrive?.clientId || '';
    const apiKey = mtPrimeLink?.gdrive?.apiKey || '';
    const accessToken = mtPrimeLink?.gdrive?.accessToken || '';
    
    // Prefer Google Picker if we have credentials
    if (clientId || apiKey) {
      openGooglePicker();
    } else {
      // Fallback to manual list if no credentials
      alert('Google Drive Client ID or API Key is required. Please configure it in Settings → Integrations.');
    }
  });

  let gdriveSearchTimeout;
  $(document).on('input', '#mt_primelink-gdrive-search-input', function(){
    clearTimeout(gdriveSearchTimeout);
    const query = $(this).val().trim();
    gdriveSearchTimeout = setTimeout(() => {
      listGdrive('root', query, '');
    }, 500);
  });

  $(document).on('click', '.mt_primelink-gdrive-listing a', function(e){
    e.preventDefault();
    const fileId = $(this).data('id');
    const fileName = $(this).data('name');
    if(fileId){
      $('#mt_primelink-gdrive-file').val(fileId);
      $('#mt_primelink-gdrive-files').html('<em style="color:#0a0;">Selected: '+escapeHtml(fileName)+'</em>');
      fetchGdriveFile();
    }
  });

  $(document).on('click', '#mt_primelink-gdrive-load-more', function(){
    const pageToken = $(this).data('page-token');
    const query = $('#mt_primelink-gdrive-search-input').val().trim();
    listGdrive('root', query, pageToken);
  });

  // OneDrive browsing functionality
  // Track navigation history for back button
  let onedriveNavigationHistory = [];
  
  async function listOnedrive(folderId, query, pageToken){
    $('#mt_primelink-onedrive-files').html('<em>Loading files...</em>');
    $('#mt_primelink-onedrive-search').show();
    
    // Store current folder in history (unless it's a search or pagination)
    if(!query && !pageToken && folderId !== 'root'){
      if(onedriveNavigationHistory.length === 0 || onedriveNavigationHistory[onedriveNavigationHistory.length - 1] !== folderId){
        onedriveNavigationHistory.push(folderId);
      }
    }
    
    try {
      const response = await restPost('/onedrive/list', {
        folder_id: folderId || 'root',
        query: query || '',
        page_token: pageToken || ''
      });
      
      if(response.files && response.files.length > 0){
        let html = '';
        
        // Add back button if not in root
        if(folderId && folderId !== 'root' && !query && !pageToken){
          html += '<button type="button" class="button button-small" id="mt_primelink-onedrive-back" style="margin-bottom:8px;">← Back</button><br>';
        }
        
        html += '<ul class="mt_primelink-onedrive-listing">';
        response.files.forEach(f => {
          const size = f.size ? formatBytes(f.size) : '';
          const isFolder = f.isFolder || false;
          const icon = isFolder ? '📁' : '📄';
          html += '<li><a href="#" data-id="'+escapeHtml(f.id)+'" data-name="'+escapeHtml(f.name)+'" data-folder="'+(isFolder ? '1' : '0')+'">';
          html += icon + ' ' + escapeHtml(f.name);
          if(size && !isFolder) html += ' <small>('+escapeHtml(size)+')</small>';
          if(isFolder) html += ' <small style="color:#666;">(folder)</small>';
          html += '</a></li>';
        });
        html += '</ul>';
        
        if(response.nextPageToken){
          html += '<button type="button" class="button button-small" id="mt_primelink-onedrive-load-more" data-page-token="'+escapeHtml(response.nextPageToken)+'">Load More</button>';
        }
        
        $('#mt_primelink-onedrive-files').html(html);
      } else {
        let msg = 'No files found. ';
        if(response.error && response.error.includes('authentication')){
          msg += 'OAuth authentication is required for browsing files. Please click "Authenticate with OneDrive" in Settings → Integrations.';
        } else {
          msg += 'Make sure you have files in your OneDrive or authenticate with OAuth.';
        }
        $('#mt_primelink-onedrive-files').html('<em>' + escapeHtml(msg) + '</em>');
      }
    } catch(err){
      const errorMsg = err.json?.error || err.json?.message || err.message || 'Failed to list OneDrive files';
      $('#mt_primelink-onedrive-files').html('<em style="color:#f00;">'+escapeHtml(errorMsg)+'</em>');
    }
  }

  $(document).on('click', '#mt_primelink-onedrive-list', function(e){
    e.preventDefault();
    e.stopPropagation();
    
    const accessToken = mtPrimeLink?.onedrive?.accessToken || '';
    if(!accessToken){
      const proceed = confirm('No OneDrive access token found. Please authenticate with OneDrive first.\n\nWould you like to:\n- Click OK to go to Settings → Integrations to authenticate\n- Click Cancel to continue without browsing');
      if(proceed){
        window.location.href = mtPrimeLink.adminUrl + 'admin.php?page=mt-primelink-settings&tab=integrations';
      }
      return;
    }
    
    listOnedrive('root', '', '');
  });

  let onedriveSearchTimeout;
  $(document).on('input', '#mt_primelink-onedrive-search-input', function(){
    clearTimeout(onedriveSearchTimeout);
    const query = $(this).val().trim();
    onedriveSearchTimeout = setTimeout(() => {
      listOnedrive('root', query, '');
    }, 500);
  });

  $(document).on('click', '.mt_primelink-onedrive-listing a', function(e){
    e.preventDefault();
    const isFolder = $(this).data('folder') === '1' || $(this).data('folder') === 1;
    const itemId = $(this).data('id');
    const itemName = $(this).data('name');
    
    if(isFolder){
      // Navigate into folder
      listOnedrive(itemId, '', '');
      return;
    }
    const fileId = $(this).data('id');
    const fileName = $(this).data('name');
    if(fileId){
      // Set the file ID in the input field
      $('#mt_primelink-onedrive-file').val(fileId);
      // Also trigger change event to ensure validation picks it up
      $('#mt_primelink-onedrive-file').trigger('change');
      $('#mt_primelink-onedrive-files').html('<em style="color:#0a0;">✓ Selected: '+escapeHtml(fileName)+'</em>');
      // Optionally fetch the file to verify it's accessible
      fetchOneDriveFile();
    }
  });

  $(document).on('click', '#mt_primelink-onedrive-load-more', function(){
    const pageToken = $(this).data('page-token');
    const query = $('#mt_primelink-onedrive-search-input').val().trim();
    const currentFolder = onedriveNavigationHistory.length > 0 ? onedriveNavigationHistory[onedriveNavigationHistory.length - 1] : 'root';
    listOnedrive(currentFolder, query, pageToken);
  });
  
  $(document).on('click', '#mt_primelink-onedrive-back', function(){
    if(onedriveNavigationHistory.length > 0){
      onedriveNavigationHistory.pop(); // Remove current folder
      const previousFolder = onedriveNavigationHistory.length > 0 ? onedriveNavigationHistory[onedriveNavigationHistory.length - 1] : 'root';
      listOnedrive(previousFolder, '', '');
    } else {
      listOnedrive('root', '', '');
    }
  });

  // Helper function to format bytes
  function formatBytes(bytes, decimals = 2) {
    if (!bytes || bytes === 0) return '0 Bytes';
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
  }

  async function fetchOneDriveFile(){
    const file = $('#mt_primelink-onedrive-file').val();
    if(!file){
      $('#mt-primelink-source-result').show().text('Please add a OneDrive item ID or link.');
      return;
    }
    let importId = currentImportId();
    try {
      if(!importId){
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id;
        setImportIdEverywhere(importId);
      }

      $('#mt-primelink-source-result').show().text('Fetching from OneDrive...');

      // Use AJAX for OneDrive (works better with POST data)
      $.ajax({
        url: mtPrimeLink.ajaxUrl,
        type: 'POST',
        data: {
          action: 'mt_primelink_import_handle_source',
          nonce: mtPrimeLink.ajaxNonce,
          import_id: importId,
          source: 'onedrive',
          path: file
        },
        success: function(response) {
          
          if(response.success && response.data && response.data.file){
            const file = response.data.file;
            if(file.original_name){
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(file.original_name)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else if(file.path){
              const fileName = file.path.split('/').pop() || file.path.split('\\').pop() || 'file';
              $('#mt-primelink-source-result')
                .html('<span style="color:#0a0;"><strong>File loaded:</strong> '+escapeHtml(fileName)+' ('+escapeHtml(file.type || 'unknown')+')</span>');
            } else {
              $('#mt-primelink-source-result').html('<span style="color:#0a0;">File loaded successfully.</span>');
            }
          } else {
            const errorMsg = response.data?.message || 'OneDrive download failed';
            $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
          }
        },
        error: function(xhr, status, error) {
          debugConsole.error('OneDrive fetch error:', xhr, status, error);
          let errorMsg = 'OneDrive download failed';
          try {
            const response = JSON.parse(xhr.responseText);
            if(response.data && response.data.message){
              errorMsg = response.data.message;
            }
          } catch(e){}
          $('#mt-primelink-source-result').html('<span style="color:#f00;">'+escapeHtml(errorMsg)+'</span>');
        }
      });

    } catch(err){
      $('#mt-primelink-source-result').show().text(err.json?.message || 'OneDrive download failed');
    }
  }

  $(document).on('click', '#mt_primelink-onedrive-fetch', function(){
    fetchOneDriveFile();
  });

  $(document).on('click', '.mt_primelink-import-back', function(){
    const url = $(this).data('url');
    if(url){
      window.location.href = url;
    } else {
      history.back();
    }
  });

  $(document).on('click', '#mt_primelink-schedule-import', async function(){
    const importId = currentImportId();
    try{
      const res = await restPost('/import/schedule', { import_id: importId });
      const when = res.run_at || 'scheduled';
      const recur = res.recurring ? (' ('+res.recurring+')') : '';
      if (window.mtPrimeLinkToast) {
        window.mtPrimeLinkToast.success('Import scheduled at ' + when + recur + '. You can manage it in Manage Imports.', 'Import Scheduled');
      } else {
      alert('Import scheduled at ' + when + recur + '. You can manage it in Manage Imports.');
      }
      window.location.href = mtPrimeLink.manageImportsUrl || (window.location.origin + '/wp-admin/admin.php?page=mt-primelink-manage-import');
    }catch(e){
      if (window.mtPrimeLinkToast) {
        window.mtPrimeLinkToast.error(e.json?.message || 'Failed to schedule import.', 'Scheduling Failed');
      } else {
      alert(e.json?.message || 'Failed to schedule import.');
      }
    }
  });

  // -----------------------------
// Step 4 Preview (REST) -> VERTICAL + ROW NAVIGATOR
// -----------------------------
let mt_primelinkPreviewIndex = 0;
let mt_primelinkPreviewTotal = 0;
let mt_primelinkPreviewHeaders = [];
let mt_primelinkPreviewRows = [];
let mt_primelinkPreviewPage = 1;
let mt_primelinkPreviewPages = 1;
let mt_primelinkPreviewPerPage = 20;
let mt_primelinkMapFields = [];
let mt_primelinkPrevFields = [];
let mt_primelinkCurrentMapIndex = 0;
let mt_primelinkSelectedProductType = 'simple';
let mt_primelinkStep5SaveTimer = null;
let mt_primelinkStep5LastSaveFailed = false;

function renderAliasRow(target, aliases){
  target = target || '';
  aliases = aliases || '';
  return ''
    + '<div class="mt_primelink-alias-row">'
    + '  <input type="text" class="mt_primelink-alias-target" placeholder="Target (e.g. post_title)" value="'+escapeHtml(target)+'" />'
    + '  <input type="text" class="mt_primelink-alias-values" placeholder="Comma separated synonyms" value="'+escapeHtml(aliases)+'" />'
    + '  <button type="button" class="button-link-delete mt_primelink-alias-remove">Remove</button>'
    + '</div>';
}

function filterFileFields(){
  const q = ($('#mt_primelink-field-search').val() || '').toLowerCase().trim();
  // Handle both import and export file fields (same class now)
  $('.mt_primelink-file-field').each(function(){
    const $field = $(this);
    const fieldKey = ($field.data('field') || '').toString().toLowerCase();
    const fieldName = ($field.find('.mt_primelink-file-field__name').text() || '').toLowerCase();
    const searchText = fieldKey + ' ' + fieldName;
    const visible = !q || searchText.indexOf(q) !== -1;
    $field.toggleClass('mt_primelink-field-hidden', !visible);
  });
}

function filterTargetSlots(){
  const q = ($('#mt_primelink-target-search').val() || '').toLowerCase().trim();
  $('.mt_primelink-map-slot').each(function(){
    const name = ($(this).find('.mt_primelink-map-label__text').text() || '').toLowerCase();
    const visible = !q || name.indexOf(q) !== -1;
    $(this).toggleClass('mt_primelink-slot-hidden', !visible);
  });
}

function markUnmappedSlots(){
  let unmapped = 0;
  $('.mt_primelink-map-slot').each(function(){
    const hasSource = !!($(this).find('.mt_primelink-map-input').val() || '').trim();
    $(this).toggleClass('unmapped', !hasSource);
    if(!hasSource){ unmapped++; }
  });
  const $pill = $('#mt_primelink-unmapped-count');
  if($pill.length){
    if(unmapped > 0){
      $pill.text(unmapped + ' unmapped');
      $pill.show();
    } else {
      $pill.hide();
    }
  }
  renderRequiredChecklist();
}

function autoMapSuggestions(){
  const sources = mt_primelinkMapFields || [];
  if(!sources.length){
    showToastOrAlert('warning', 'No source fields detected yet.', 'Mapping');
    return;
  }

  const norm = s => (s || '').toString().toLowerCase().replace(/[^a-z0-9]+/g, '');

  // Aliases cover multiple languages/variations for targets
  const targetAliases = {
    post_title:   ['title','name','titlu','titel','productname','produktname'],
    post_content: ['description','descriere','beschreibung','desc','content','beschreibunglang','longdescription'],
    post_excerpt: ['shortdescription','short_desc','kurzbeschreibung','descriere scurta','excerpt'],
    sku:          ['sku','cod','code','artikelnummer','artnr','productcode'],
    price:        ['price','pret','preis','cost','amount','valor','betrag'],
    regular_price:['regularprice','pretstandard','listenpreis','preisregular'],
    sale_price:   ['saleprice','pretpromo','angebotspreis','discountprice'],
    stock:        ['stock','stoc','lagerbestand','qty','quantity','cantitate'],
    image:        ['image','imagine','bild','photo','mainimage'],
    images:       ['images','imagesimage','image','imagine','bilder','photos','gallery','galerie','imagini'],
    gallery:      ['gallery','galerie','bilder','imagini','images']
  };
  // Merge custom aliases
  $('.mt_primelink-alias-row').each(function(){
    const target = ($(this).find('.mt_primelink-alias-target').val() || '').trim();
    const vals = ($(this).find('.mt_primelink-alias-values').val() || '').split(',').map(v=>v.trim()).filter(Boolean);
    if(!target || !vals.length) return;
    if(!targetAliases[target]) targetAliases[target] = [];
    targetAliases[target] = targetAliases[target].concat(vals.map(v=>norm(v)));
  });

  const normalizedSources = sources.map(s => ({raw:s, n:norm(s)}));

  function similarity(a,b){
    // Levenshtein distance normalized
    const la = a.length, lb = b.length;
    if(!la || !lb) return 0;
    const dp = Array.from({length: la+1}, () => new Array(lb+1).fill(0));
    for(let i=0;i<=la;i++) dp[i][0]=i;
    for(let j=0;j<=lb;j++) dp[0][j]=j;
    for(let i=1;i<=la;i++){
      for(let j=1;j<=lb;j++){
        const cost = a[i-1] === b[j-1] ? 0 : 1;
        dp[i][j] = Math.min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+cost);
      }
    }
    const dist = dp[la][lb];
    return 1 - (dist / Math.max(la, lb));
  }

  function findBestMatch(target){
    const nTarget = norm(target);
    let best = { score: 0, source: null };

    // Special handling for images - prefer images.image[] if available
    if (target === 'images') {
      // First try to find images.image[] field
      const imagesArrayField = sources.find(s => s === 'images.image[]' || s.indexOf('images.image[]') === 0);
      if (imagesArrayField) {
        return { score: 1, source: imagesArrayField };
      }
      // Then try images field
      const imagesField = sources.find(s => s === 'images');
      if (imagesField) {
        return { score: 0.95, source: imagesField };
      }
      // Then try any images.image.* field
      const imageIndexedFields = sources.filter(s => s.indexOf('images.image.') === 0);
      if (imageIndexedFields.length > 0) {
        // Use images.image[] as the source (will be collected automatically)
        return { score: 0.9, source: 'images.image[]' };
      }
    }

    normalizedSources.forEach(({raw,n}) => {
      // Exact match first
      if(n === nTarget && best.score < 1){
        best = { score: 1, source: raw };
        return;
      }
    });
    if(best.source) return best;

    // Alias-based
    const aliases = targetAliases[target] || [];
    normalizedSources.forEach(({raw,n}) => {
      if(aliases.includes(n)){
        best = { score: 0.99, source: raw };
      }
    });
    if(best.source) return best;

    // Fuzzy match
    normalizedSources.forEach(({raw,n}) => {
      const score = similarity(n, nTarget);
      if(score > best.score){
        best = { score, source: raw };
      }
    });
    if(best.score >= 0.65){
      return best;
    }
    return { score:0, source:null };
  }

  let mappedCount = 0;
  $('.mt_primelink-map-slot').each(function(){
    const $slot = $(this);
    const target = $slot.data('target') || '';
    const $input = $slot.find('.mt_primelink-map-input');
    if(($input.val() || '').trim()){
      return; // already mapped
    }

    const match = findBestMatch(target);
    if(match.source){
      $slot.find('.mt-map-slot__drop').html(buildChip(match.source));
      $input.val(match.source);
      setSlotBadge($slot, 'auto');
      $slot.find('.mt_primelink-map-edit').prop('disabled', false).addClass('is-visible');
      mappedCount++;
    }
  });

  if(mappedCount){
    saveStep5Mapping(true);
    markUnmappedSlots();
    loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
    showToastOrAlert('success', 'Auto-map applied to ' + mappedCount + ' slots.', 'Auto-Map Applied');
  } else {
    showToastOrAlert('info', 'No auto-map matches found.', 'Auto-Map');
  }
}

function renderVerticalPreview($preview){
  const headers = mt_primelinkPreviewHeaders;
  const rows = mt_primelinkPreviewRows;
  const totalPage = rows.length;
  const totalAll = mt_primelinkPreviewTotal;

  if (!headers.length || totalPage < 1) {
    $preview.html('<div class="mt-empty">No preview data.</div>');
    return;
  }

  if (mt_primelinkPreviewIndex < 0) mt_primelinkPreviewIndex = 0;
  if (mt_primelinkPreviewIndex > totalPage - 1) mt_primelinkPreviewIndex = totalPage - 1;

  const row = rows[mt_primelinkPreviewIndex] || [];
  const absoluteIndex = ((mt_primelinkPreviewPage - 1) * mt_primelinkPreviewPerPage) + mt_primelinkPreviewIndex + 1;

  let html = '';
  html += '<div class="mt-preview-meta"><strong>Type:</strong> ' + escapeHtml($preview.data('type') || '') + '</div>';

  // Navigator
  html += '<div class="mt-preview-nav">';
  html += '  <button type="button" class="button mt-preview-prev" ' + (mt_primelinkPreviewIndex <= 0 ? 'disabled' : '') + '>&lt; Prev</button>';
  html += '  <span class="mt-preview-counter">Row ' + absoluteIndex + ' / ' + totalAll + '</span>';
  html += '  <button type="button" class="button button-primary mt-preview-next" ' + (mt_primelinkPreviewIndex >= totalPage - 1 ? 'disabled' : '') + '>Next &gt;</button>';
  html += '</div>';

  // Vertical table
  html += '<div class="mt-preview-vertical-wrap">';
  html += '<table class="mt-preview-vertical">';
  html += '<thead><tr><th>Field</th><th>Value</th></tr></thead><tbody>';

  headers.forEach((field, i) => {
    // Ensure field is a string
    const fieldName = (field !== null && field !== undefined) ? String(field) : '';
    if (!fieldName) return; // Skip invalid fields
    
    // Rows are now indexed arrays (converted from associative arrays in PHP)
    // Values are in the same order as headers
    let value = (row[i] !== undefined && row[i] !== null) ? String(row[i]) : '';
    
    // Special handling for images fields - show as image gallery
    if (fieldName.indexOf('images.image') === 0 || fieldName === 'images' || fieldName === 'images.image[]') {
      // Check if there are multiple image fields (images.image.0, images.image.1, etc.)
      const imageUrls = [];
      if (fieldName === 'images' || fieldName === 'images.image[]') {
        // Try to collect all images.image.* fields
        let idx = 0;
        const fieldIndex = headers.indexOf('images.image.' + idx);
        while (fieldIndex >= 0 || idx === 0) {
          const imgField = 'images.image.' + idx;
          const imgIndex = headers.indexOf(imgField);
          if (imgIndex >= 0 && row[imgIndex]) {
            const imgUrl = String(row[imgIndex]).trim();
            if (imgUrl) imageUrls.push(imgUrl);
          }
          idx++;
          if (idx > 20) break; // Safety limit
          if (headers.indexOf('images.image.' + idx) < 0 && idx > 0) break;
        }
        // If no indexed fields found, use the main field value
        if (imageUrls.length === 0 && value) {
          // Split by comma if it's a comma-separated string
          const urls = value.split(',').map(u => u.trim()).filter(u => u);
          imageUrls.push(...urls);
        }
      } else if (value) {
        // Single image field
        imageUrls.push(value);
      }
      
      // Render images as gallery
      if (imageUrls.length > 0) {
        let imgHtml = '<div class="mt_primelink-preview-images" style="display:flex;flex-wrap:wrap;gap:8px;margin-top:4px;">';
        imageUrls.forEach(url => {
          const cleanUrl = url.trim();
          if (cleanUrl) {
            imgHtml += '<div class="mt_primelink-preview-image" style="position:relative;width:80px;height:80px;border:1px solid rgba(255,255,255,0.2);border-radius:4px;overflow:hidden;background:#f0f0f0;">';
            imgHtml += '<img src="' + escapeHtml(cleanUrl) + '" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display=\'none\';this.parentElement.innerHTML=\'<div style=\\\'padding:8px;text-align:center;font-size:10px;color:#999\\\'>Image<br/>Error</div>\';" />';
            imgHtml += '</div>';
          }
        });
        imgHtml += '</div>';
        if (imageUrls.length > 1) {
          imgHtml += '<small class="muted" style="display:block;margin-top:4px;">' + imageUrls.length + ' images</small>';
        }
        value = imgHtml;
      } else {
        value = '<span class="muted">No images</span>';
      }
    }
    
    html += '<tr>';
    html += '<td class="mt-preview-field">' + escapeHtml(fieldName) + '</td>';
    html += '<td class="mt-preview-value">' + value + '</td>';
    html += '</tr>';
  });

  html += '</tbody></table></div>';

  $preview.html(html);
}

function updatePreviewPaginationUI(){
  $('#mt_primelink-preview-page-label').text(mt_primelinkPreviewPage + ' / ' + mt_primelinkPreviewPages);
  $('#mt_primelink-preview-page-prev').prop('disabled', mt_primelinkPreviewPage <= 1);
  $('#mt_primelink-preview-page-next').prop('disabled', mt_primelinkPreviewPage >= mt_primelinkPreviewPages);
}

async function loadPreview(){
  const $preview = $('#mt-primelink-preview');
  if(!$preview.length) return;

  const importId = parseInt($preview.data('import-id'), 10) || currentImportId();
  const xpathVal = $('#mt-primelink-xpath').val() || '';
  mt_primelinkPreviewPerPage = parseInt($('#mt_primelink-preview-limit').val(), 10) || 20;
  if(mt_primelinkPreviewPerPage < 1) mt_primelinkPreviewPerPage = 1;

  try {
    const res = await restPost('/import/preview', {
      import_id: importId,
      xpath: xpathVal,
      filters: collectFilterRowsData(),
      page: mt_primelinkPreviewPage,
      per_page: mt_primelinkPreviewPerPage
    });

    // Handle both direct preview response and nested preview structure
    const p = res.preview || res || {};
    const headers = p.headers || [];
    const rows = p.rows || [];
    

    mt_primelinkPreviewHeaders = headers;
    mt_primelinkPreviewRows = rows;
    mt_primelinkPreviewTotal = p.total_filtered || p.total || rows.length || 0;
    mt_primelinkPreviewPages = Math.max(1, Math.ceil(mt_primelinkPreviewTotal / mt_primelinkPreviewPerPage));
    
    // Check if preview_index is in URL (for navigation from anomalies modal)
    const urlParams = new URLSearchParams(window.location.search);
    const previewIndexParam = urlParams.get('preview_index');
    if (previewIndexParam !== null && !isNaN(previewIndexParam)) {
      const targetIndex = parseInt(previewIndexParam, 10);
      // Calculate which page this index is on (0-based index)
      const targetPage = Math.floor(targetIndex / mt_primelinkPreviewPerPage) + 1;
      if (targetPage <= mt_primelinkPreviewPages) {
        mt_primelinkPreviewPage = targetPage;
        mt_primelinkPreviewIndex = targetIndex % mt_primelinkPreviewPerPage;
        // Remove the parameter from URL to avoid reapplying on refresh
        urlParams.delete('preview_index');
        const newUrl = window.location.pathname + (urlParams.toString() ? '?' + urlParams.toString() : '') + window.location.hash;
        window.history.replaceState({}, '', newUrl);
      } else {
        mt_primelinkPreviewIndex = 0;
      }
    } else {
      mt_primelinkPreviewIndex = 0;
    }
    
    if(mt_primelinkPreviewPage > mt_primelinkPreviewPages){
      mt_primelinkPreviewPage = mt_primelinkPreviewPages;
      return loadPreview();
    }

    $preview.data('type', res.type || '');

    if (!mt_primelinkPreviewTotal) {
      $preview.html('<div class="mt-empty">No rows found.</div>');
      updatePreviewPaginationUI();
      return;
    }

    renderVerticalPreview($preview);
    updatePreviewPaginationUI();

    // If XML, try to fetch roots/tags for filter builder
    if((res.type || '').toLowerCase() === 'xml'){
      fetchXmlRoots(importId);
    } else {
      $('#mt_primelink-xml-tags').empty();
    }

  } catch(err){
    let errMsg = 'Unknown error';
    if(err && typeof err === 'object'){
      if(err.json && err.json.message){
        errMsg = err.json.message;
      } else if(err.json && typeof err.json === 'object'){
        errMsg = JSON.stringify(err.json, null, 2);
      } else if(err.message){
        errMsg = err.message;
      } else if(err.status){
        errMsg = `HTTP ${err.status}: ${err.json ? JSON.stringify(err.json) : 'Request failed'}`;
      }
    } else if(err){
      errMsg = String(err);
    }
    $preview.html('Preview failed.<br><pre>'+escapeHtml(errMsg)+'</pre>');
    debugConsole.error('Preview error:', err);
  }
}

async function fetchXmlRoots(importId){
  const $root = $('#mt_primelink-xml-root');
  if(!$root.length) return;
  try{
    const res = await restPost('/import/xml-roots', { import_id: importId });
    const roots = res.roots || [];
    let html = '<option value=\"\">Select root...</option>';
    roots.slice(0,50).forEach(r=>{
      const path = r.path || '';
      const label = path + (r.count ? ' ('+r.count+')' : '');
      html += '<option value=\"'+escapeHtml(path)+'\">'+escapeHtml(label)+'</option>';
    });
    $root.html(html);
    // auto-select if xpath matches
    const current = $('#mt-primelink-xpath').val();
    if(current){
      $root.val(current);
    }
  } catch(e){
    // ignore
  }
}

async function fetchXmlTags(importId, root){
  const $tags = $('#mt_primelink-xml-tags');
  if(!$tags.length || !root) { $tags.empty(); return; }
  try{
    const res = await restPost('/import/xml-tags', { import_id: importId, root });
    const tags = res.tags || [];
    if(!tags.length){
      $tags.html('<div class=\"mt_primelink-empty\">No tags detected under this root.</div>');
      return;
    }
    let html = '<div class=\"mt_primelink-summary-subtitle\">'+escapeHtml('Available tags')+'</div>';
    html += '<ul class=\"mt_primelink-summary-list mt_primelink-xml-tags-list\">';
    tags.slice(0,50).forEach(t=>{
      html += '<li>';
      const label = (t.tag || '') + (t.count ? ' ('+t.count+')' : '');
      html += '<button type=\"button\" class=\"button mt_primelink-xml-tag-btn\" data-tag=\"'+escapeHtml(t.tag || '')+'\">'+escapeHtml(label)+'</button>';
      html += '</li>';
    });
    html += '</ul>';
    $tags.html(html);
    updateFilterUI();
  } catch(e){
    $tags.html('<div class=\"mt_primelink-empty\">Tags could not be loaded.</div>');
  }
}

// Navigator events
$(document).on('click', '.mt-preview-prev', function(){
  const $preview = $('#mt-primelink-preview');
  if (mt_primelinkPreviewIndex > 0) {
    mt_primelinkPreviewIndex--;
    renderVerticalPreview($preview);
  }
});

$(document).on('click', '.mt-preview-next', function(){
  const $preview = $('#mt-primelink-preview');
  if (mt_primelinkPreviewIndex < mt_primelinkPreviewTotal - 1) {
    mt_primelinkPreviewIndex++;
    renderVerticalPreview($preview);
  }
});

$(document).on('click', '#mt_primelink-preview-page-prev', function(){
  if(mt_primelinkPreviewPage > 1){
    mt_primelinkPreviewPage--;
    mt_primelinkPreviewIndex = 0;
    loadPreview();
  }
});

$(document).on('click', '#mt_primelink-preview-page-next', function(){
  if(mt_primelinkPreviewPage < mt_primelinkPreviewPages){
    mt_primelinkPreviewPage++;
    mt_primelinkPreviewIndex = 0;
    loadPreview();
  }
});

$(document).on('change', '#mt_primelink-preview-limit', function(){
  mt_primelinkPreviewPage = 1;
  mt_primelinkPreviewIndex = 0;
  loadPreview();
});


$(document).on('input', '#mt-primelink-xpath', function(){
  clearTimeout(window.mt_primelinkXPathTimer);
  window.mt_primelinkXPathTimer = setTimeout(loadPreview, 500);
});

// Detect roots
$(document).on('click', '#mt_primelink-detect-xml-roots', function(){
  const importId = currentImportId();
  fetchXmlRoots(importId);
});

// On root change, set xpath field and load tags
$(document).on('change', '#mt_primelink-xml-root', function(){
  const val = $(this).val();
  if(val){
    $('#mt-primelink-xpath').val(val);
    const importId = currentImportId();
    fetchXmlTags(importId, val);
    loadPreview();
  }
});

// Add filter rows
let mt_primelinkFilterIdx = 0;
function addFilterRow(field='', op='=', value=''){
  const idx = mt_primelinkFilterIdx++;
  const actions = [
    {v:'contains', l:'Contains', op:'contains'},
    {v:'not_contains', l:'Does not contain', op:'not_contains'},
    {v:'equals', l:'Equals', op:'='},
    {v:'not_equals', l:'Not equals', op:'!='},
    {v:'gt', l:'Greater than', op:'>'},
    {v:'lt', l:'Less than', op:'<'},
    {v:'empty', l:'Is empty', op:'='},
    {v:'not_empty', l:'Is not empty', op:'!='}
  ];
  const actionFromOp = (opVal, val) => {
    if(opVal === '=' && val === '') return 'empty';
    if(opVal === '!=' && val === '') return 'not_empty';
    if(opVal === 'contains') return 'contains';
    if(opVal === 'not_contains') return 'not_contains';
    if(opVal === '>') return 'gt';
    if(opVal === '<') return 'lt';
    if(opVal === '!=') return 'not_equals';
    return 'equals';
  };
  const act = actionFromOp(op, value);
  let html = '<div class="mt_primelink-filter-row" data-idx="'+idx+'">';
  html += '<div class="mt_primelink-filter-drop mt-map-slot__drop">'+(field ? buildChip(field) : '<span class="mt_primelink-muted">Drop field</span>')+'</div>';
  html += '<input type="hidden" class="mt_primelink-filter-field" name="filters['+idx+'][field]" value="'+escapeHtml(field)+'" />';
  html += '<select class="mt_primelink-filter-action" data-idx="'+idx+'">';
  actions.forEach(a=>{ html += '<option value="'+a.v+'" '+(a.v===act?'selected':'')+'>'+a.l+'</option>'; });
  html += '</select>';
  html += '<select name="filters['+idx+'][op]" class="mt_primelink-filter-op" style="display:none;">';
  actions.forEach(a=>{ html += '<option value="'+a.op+'" '+(a.op===op?'selected':'')+'>'+a.op+'</option>'; });
  html += '</select>';
  html += '<div class="mt_primelink-filter-inline">';
  html += '<input type="text" class="widefat mt_primelink-filter-value" name="filters['+idx+'][value]" placeholder="value" value="'+escapeHtml(value)+'" />';
  html += '<button type="button" class="button mt_primelink-filter-remove">x</button>';
  html += '</div>';
  html += '<div class="mt_primelink-filter-outcome">Rows are kept only when the condition is TRUE; others are skipped.</div>';
  html += '</div>';
  $("#mt_primelink-filter-rows").append(html);
  applyFilterAction($("#mt_primelink-filter-rows .mt_primelink-filter-row").last(), act, value);
}

function applyFilterAction($row, action, currentVal){
  const $op = $row.find('.mt_primelink-filter-op');
  const $val = $row.find('.mt_primelink-filter-value');
  let opVal = '=';
  let needsVal = true;
  switch(action){
    case 'contains': opVal='contains'; break;
    case 'not_contains': opVal='not_contains'; break;
    case 'gt': opVal='>'; break;
    case 'lt': opVal='<'; break;
    case 'not_equals': opVal='!='; break;
    case 'empty': opVal='='; needsVal=false; currentVal=''; break;
    case 'not_empty': opVal='!='; needsVal=false; currentVal=''; break;
    default: opVal='='; break;
  }
  $op.val(opVal);
  if(needsVal){
    $val.prop('disabled', false).removeClass('is-disabled');
    if(currentVal !== undefined){ $val.val(currentVal); }
  } else {
    $val.val('');
    $val.prop('disabled', true).addClass('is-disabled');
  }
}

$(document).on('click', '#mt_primelink-add-filter-row', function(){
  addFilterRow();
  updateFilterUI();
});

// Change action
$(document).on('change', '.mt_primelink-filter-action', function(){
  const $row = $(this).closest('.mt_primelink-filter-row');
  const action = $(this).val();
  const val = $row.find('.mt_primelink-filter-value').val();
  applyFilterAction($row, action, val);
  updateFilterUI();
  if(typeof loadMappingPreview === 'function'){
    loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
  }
});

$(document).on('click', '.mt_primelink-filter-remove', function(){
  $(this).closest('.mt_primelink-filter-row').remove();
  updateFilterUI();
});

// Clicking a tag adds a row prefilled
$(document).on('click', '.mt_primelink-xml-tag-btn', function(){
  const tag = $(this).data('tag') || '';
  addFilterRow(tag, 'contains', '');
  updateFilterUI();
});

// Drag/drop for filter field
$(document).on('dragover dragenter', '.mt_primelink-filter-drop', function(ev){
  ev.preventDefault();
  $(this).addClass('is-over');
});
$(document).on('dragleave', '.mt_primelink-filter-drop', function(){
  $(this).removeClass('is-over');
});
$(document).on('drop', '.mt_primelink-filter-drop', function(ev){
  ev.preventDefault();
  $(this).removeClass('is-over');
  const field = ev.originalEvent.dataTransfer.getData('text/plain');
  if(!field) return;
  $(this).html(buildChip(field));
  $(this).closest('.mt_primelink-filter-row').find('.mt_primelink-filter-field').val(field);
  updateFilterUI();
  if(typeof loadMappingPreview === 'function'){
    loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
  }
});

function collectFilterFields(){
  const fields = [];
  $('.mt_primelink-filter-row').each(function(){
    const field = ($(this).find('input[name*=\"[field]\"]').val() || '').trim();
    if(field){
      fields.push(field);
    }
  });
  return fields;
}

function collectFilterRowsData(){
  const filters = [];
  $('.mt_primelink-filter-row').each(function(){
    const field = ($(this).find('input[name*=\"[field]\"]').val() || '').trim();
    const op    = ($(this).find('select[name*=\"[op]\"]').val() || '').trim();
    const value = ($(this).find('input[name*=\"[value]\"]').val() || '').trim();
    if(field || value){
      filters.push({ field, op, value });
    }
  });
  return filters;
}

function updateFilterUI(){
  const fields = collectFilterFields();
  const $badge = $('#mt_primelink-filter-active-count');
  const active = fields.length;

  if(active > 0){
    $badge.text(active + ' active');
    $badge.show();
  } else {
    $badge.hide();
  }

  const fieldSet = new Set(fields.map(f => f.toLowerCase()));
  $('.mt_primelink-xml-tag-btn').each(function(){
    const tag = ($(this).data('tag') || '').toString().toLowerCase();
    if(fieldSet.has(tag)){
      $(this).addClass('is-filtered');
    } else {
      $(this).removeClass('is-filtered');
    }
  });

  const $notice = $('#mt_primelink-filter-notice');
  let warn = '';
  $('.mt_primelink-filter-row').each(function(){
    const field = ($(this).find('input[name*=\"[field]\"]').val() || '').trim();
    const value = ($(this).find('input[name*=\"[value]\"]').val() || '').trim();
    if(!field && value){
      warn = 'Complete the tag field for filters with a value.';
    }
  });
  if(warn){
    $notice.text(warn).show();
  } else {
    $notice.hide();
  }
  if(typeof loadMappingPreview === 'function'){
    // Use live preview for automatic updates
    triggerLivePreview();
  }
}

$(document).on('input change', '.mt_primelink-filter-row input, .mt_primelink-filter-row select', function(){
  updateFilterUI();
});

// Filter presets
async function fetchFilterPresets(){
  try{
    const res = await restGet('/import/filter-presets');
    const list = res.presets || [];
    const $sel = $('#mt_primelink-filter-presets');
    if(!$sel.length) return;
    let html = '<option value="">Presets</option>';
    list.forEach(name => {
      html += '<option value="'+escapeHtml(name)+'">'+escapeHtml(name)+'</option>';
    });
    $sel.html(html);
  }catch(e){
    // silent
  }
}

async function loadFilterPreset(name){
  if(!name) return;
  try{
    const res = await restPost('/import/filter-presets/load', { name });
    const filters = res.filters || [];
    $('#mt_primelink-filter-rows').empty();
    mt_primelinkFilterIdx = 0;
    filters.forEach(f => addFilterRow(f.field || '', f.op || '=', f.value || ''));
    if(!filters.length){
      addFilterRow();
    }
    updateFilterUI();
  }catch(e){
    alert(e.json?.message || 'Could not load preset');
  }
}

async function saveFilterPreset(){
  const name = prompt('Preset name?');
  if(!name) return;
  const filters = collectFilterRowsData();
  try{
    await restPost('/import/filter-presets/save', { name, filters });
    fetchFilterPresets();
    alert('Preset saved');
  }catch(e){
    alert(e.json?.message || 'Could not save preset');
  }
}

async function deleteFilterPreset(){
  const $sel = $('#mt_primelink-filter-presets');
  const name = $sel.val();
  if(!name) return;
  if(!confirm('Delete preset "'+name+'"?')) return;
  try{
    await restPost('/import/filter-presets/delete', { name });
    fetchFilterPresets();
    alert('Preset deleted');
  }catch(e){
    alert(e.json?.message || 'Could not delete preset');
  }
}

$(document).on('click', '#mt_primelink-filter-preset-load', function(){
  const name = $('#mt_primelink-filter-presets').val();
  loadFilterPreset(name);
});

$(document).on('click', '#mt_primelink-filter-preset-save', function(){
  saveFilterPreset();
});

$(document).on('click', '#mt_primelink-filter-preset-delete', function(){
  deleteFilterPreset();
});

// Auto-map button (Step 5 for import, Step 2 for export)
$(document).on('click', '#mt_primelink-auto-map', function(){
  // Check if we're in export step 2 or import step 5
  if($('.mt_primelink-export-step-2').length){
    autoMapExportSuggestions();
  } else {
    autoMapSuggestions();
  }
});

// Clear all mapping slots
$(document).on('click', '#mt_primelink-clear-mapping', function(){
  if(!confirm('Clear all mapped fields?')) return;
  
  // Check if we're in export step 2
  const isExport = $('.mt_primelink-export-step-2').length > 0;
  
  $('.mt_primelink-map-slot').each(function(){
    $(this).find('.mt-map-slot__drop').html('<span class="mt_primelink-muted">Drop field here</span>');
    $(this).find('.mt_primelink-map-input').val('');
    $(this).find('.mt_primelink-map-config').val('');
    $(this).find('.mt_primelink-map-edit').prop('disabled', true).removeClass('is-visible');
    if(typeof setSlotBadge === 'function'){
      setSlotBadge($(this), '');
    }
  });
  $('.mt_primelink-attr-row').remove();
  $('#mt_primelink-custom-fields-wrap').empty();
  
  if(isExport){
    saveExportStep2Mapping();
    setTimeout(() => {
      loadExportPreview();
    }, 300);
  } else {
    saveStep5Mapping(true);
    markUnmappedSlots();
    if(typeof loadMappingPreview === 'function'){
      loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
    }
  }
});
  // -----------------------------
  // Step 5 Mapping preview (REST)
  // -----------------------------
  function bindDnD(){
    $('.mt-field, .mt_primelink-file-field').off('dragstart').on('dragstart', function(ev){
      const field = $(this).data('field');
      const label = $(this).text();
      ev.originalEvent.dataTransfer.setData('text/plain', field || label || '');
    });

    // Use event delegation for drop zones to support dynamically added elements
    $(document).off('dragover.dropzone drop.dropzone dragleave.dropzone', '.mt-map-slot__drop')
      .on('dragover.dropzone', '.mt-map-slot__drop', function(ev){ ev.preventDefault(); $(this).addClass('is-over'); })
      .on('dragleave.dropzone', '.mt-map-slot__drop', function(){ $(this).removeClass('is-over'); })
      .on('drop.dropzone', '.mt-map-slot__drop', function(ev){
      ev.preventDefault();
      $(this).removeClass('is-over');

      const dt = ev.originalEvent.dataTransfer;
      let field = dt ? dt.getData('text/plain') : '';
      if(!field || field === 'undefined'){
        const maybe = $(ev.originalEvent.target).closest('[data-field]');
        field = maybe.data('field') || maybe.text() || '';
      }
      field = String(field || '').trim();
      if (!field || field === 'undefined') {
        return;
      }

      const $dropZone = $(this);
      // Check if this is inside an attribute row (attributes have .mt_primelink-attr-row parent)
      const $attr = $dropZone.closest('.mt_primelink-attr-row');
      // Check if this is inside a custom field (custom fields have .mt_primelink-custom-field parent)
      const $customField = $dropZone.closest('.mt_primelink-custom-field');
      // Check if this is inside custom export columns wrap
      const $customExportWrap = $dropZone.closest('#mt_primelink-custom-export-columns-wrap');
      // Check if this is inside a regular map slot (slots have .mt_primelink-map-slot parent)
      // For attributes, the drop zone is inside .mt_primelink-map-slot, but we need to check if it's inside .mt_primelink-attr-row
      const $slot = $dropZone.closest('.mt_primelink-map-slot');
      
      // Handle custom fields first
      if($customField.length){
        // Check if this slot is inside a custom field (not a regular slot or attribute)
        const isCustomFieldSlot = $customField.find('.mt_primelink-map-slot').is($slot) || $slot.closest('.mt_primelink-custom-field').length > 0;
        
        if(isCustomFieldSlot && !$attr.length){
          // Save the field to the hidden input
          const $customVal = $customField.find('.mt_primelink-custom-val');
          if($customVal.length){
            $customVal.val(field);
          }
          
          // Update the drop zone display with chip
          $dropZone.html(buildChip(field));
          // Remove unmapped class
          $slot.removeClass('unmapped');
          pushMapHistory();
          saveStep5Mapping();
          // Reload preview to show the mapped value (with live preview)
          triggerLivePreview();
          return; // Exit early for custom fields
        }
      }
      
      // Handle attribute rows - check if drop zone is inside an attribute row
      // For attributes, the drop zone is inside .mt_primelink-map-slot which is inside .mt_primelink-attr-row
      if($attr.length){
        // Check if this slot is inside an attribute row (not a regular slot)
        const isAttributeSlot = $attr.find('.mt_primelink-map-slot').is($slot) || $slot.closest('.mt_primelink-attr-row').length > 0;
        
        if(isAttributeSlot){
        // Save the field to the hidden input
        const $attrSource = $attr.find('.mt_primelink-attr-source');
        if($attrSource.length){
          $attrSource.val(field);
        }
        
        // Update the drop zone display with chip
        $dropZone.html(buildChip(field));
        // Remove unmapped class and enable Edit button
        const $attrSlot = $attr.find('.mt_primelink-map-slot');
        $attrSlot.removeClass('unmapped');
        $attr.find('.mt_primelink-attr-edit').prop('disabled', false).addClass('is-visible');
        pushMapHistory();
        saveStep5Mapping();
        // Reload preview to show the mapped value (with live preview)
        triggerLivePreview();
        return; // Exit early for attributes
        }
      }
      
      // Handle custom export columns
      if($customExportWrap.length && $slot.length){
        // Save the field to the hidden input
        const $input = $slot.find('.mt_primelink-map-input');
        if($input.length){
          $input.val(field);
        }
        
        // Update the drop zone display with chip
        $dropZone.html(buildChip(field));
        // Remove unmapped class
        $slot.removeClass('unmapped');
        
        // Save export mapping
        saveExportStep2Mapping();
        // Reload preview
        loadExportPreview();
        return; // Exit early for custom export columns
      }
      
      const target = $slot.length ? $slot.data('target') || '' : '';

      if($slot.length){
        const existing = ($slot.find('.mt_primelink-map-input').val() || '').trim();
        const cfg = getSlotConfig($slot) || {};
        const isGallery = (target === 'images' || target === 'variation_gallery');

        if(existing && isGallery && existing !== field){
          let extras = Array.isArray(cfg.extra_sources) ? cfg.extra_sources.slice() : [];
          const all = [existing].concat(extras || []);
          if(all.indexOf(field) === -1){
            all.push(field);
          }
          $slot.find('.mt_primelink-map-input').val(all[0]);
          cfg.extra_sources = all.slice(1);
          setSlotConfig($slot, cfg);

          const chips = all.map((f, idx) => buildChip(f)).join(' ');
          $dropZone.html(chips);
        } else {
          $dropZone.html(buildChip(field));
          $slot.find('.mt_primelink-map-input').val(field);
          cfg.source_mode = 'manual';
          setSlotConfig($slot, cfg);
          setSlotBadge($slot, 'manual');
        }

        $slot.find('.mt_primelink-map-edit').prop('disabled', false).addClass('is-visible');

        pushMapHistory();
        saveStep5Mapping();
        markUnmappedSlots();
        // Trigger live preview update
        triggerLivePreview();
      }
    });

    // remove chip
  $(document).off('click', '.mt_primelink-chip-remove').on('click', '.mt_primelink-chip-remove', function(e){
    e.preventDefault();
    const $slot = $(this).closest('.mt_primelink-map-slot');
    const $attr = $(this).closest('.mt_primelink-attr-row');
    const $drop = $slot.length ? $slot.find('.mt-map-slot__drop') : $(this).closest('.mt-map-slot__drop');
    const isExport = $slot.closest('.mt_primelink-export-step-2').length > 0;

    if($slot.length){
      $drop.html('<span class="mt_primelink-muted">Drop field here</span>');
      $slot.find('.mt_primelink-map-input').val('');
      setSlotConfig($slot, {});
      setSlotBadge($slot, '');

      // Add unmapped class for custom export columns when source is removed
      const $customExportWrap = $slot.closest('#mt_primelink-custom-export-columns-wrap');
      if($customExportWrap.length){
        $slot.addClass('unmapped');
      }

      // Disable Edit again
      $slot.find('.mt_primelink-map-edit').prop('disabled', true);
      $slot.find('.mt_primelink-map-edit').removeClass('is-visible');

      if(isExport){
        saveExportStep2Mapping();
        loadExportPreview();
      } else {
        pushMapHistory();
        saveStep5Mapping();
        markUnmappedSlots();
        // Trigger live preview update
        triggerLivePreview();
      }
    } else if($attr.length){
      $drop.html('<span class="mt_primelink-muted">Drop field here</span>');
      $attr.find('.mt_primelink-attr-source').val('');
      // Add unmapped class and disable Edit button
      const $attrSlot = $attr.find('.mt_primelink-map-slot');
      $attrSlot.addClass('unmapped');
      $attr.find('.mt_primelink-attr-edit').prop('disabled', true).removeClass('is-visible');
      if(isExport){
        saveExportStep2Mapping();
      } else {
        pushMapHistory();
        saveStep5Mapping();
      }
    } else if($customField.length){
      $drop.html('<span class="mt_primelink-muted">Drop field here</span>');
      $customField.find('.mt_primelink-custom-val').val('');
      $customField.find('.mt_primelink-map-config').val(''); // Clear config
      // Add unmapped class
      const $customSlot = $customField.find('.mt_primelink-map-slot');
      $customSlot.addClass('unmapped');
      if(isExport){
        saveExportStep2Mapping();
      } else {
        pushMapHistory();
        saveStep5Mapping();
        loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
      }
    }
  });

  // Open slot editor when clicking the "edit" label on chip
  $(document).on('click', '.mt_primelink-chip-edit-label', function(e){
    e.preventDefault();
    const $slot = $(this).closest('.mt_primelink-map-slot');
    if($slot.length && typeof openEditor === 'function'){
      openEditor($slot);
    }
  });

  // Apply suggestion pill
  $(document).on('click', '.mt_primelink-suggestion-action', function(){
    const target = $(this).data('target');
    const source = $(this).data('source');
    if(target && source){
      applyMapping(target, source);
      pushMapHistory();
      saveStep5Mapping();
      markUnmappedSlots();
      loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
    }
  });
  // Edit specific chip
  $(document).on('click', '.mt_primelink-chip-edit-label', function(e){
    e.preventDefault();
    const $slot = $(this).closest('.mt_primelink-map-slot');
    const src   = $(this).closest('.mt_primelink-chip').data('source');
    if($slot.length && typeof openEditor === 'function'){
      openEditor($slot, src);
    }
  });

  // Clear mapping from suggestion pill
  $(document).on('click', '.mt_primelink-suggestion-clear', function(e){
    e.preventDefault();
    const target = $(this).data('target');
    const $slot = $('.mt_primelink-map-slot[data-target="'+target+'"]');
    if($slot.length){
      $slot.find('.mt_primelink-map-input').val('');
      $slot.find('.mt_primelink-map-drop').html('<span class="mt_primelink-muted">Drop field here</span>');
      $slot.find('.mt_primelink-map-edit').prop('disabled', true).removeClass('is-visible');
      setSlotBadge($slot, '');
      pushMapHistory();
      saveStep5Mapping();
      markUnmappedSlots();
      loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
    }
  });

  // Toggle nested field groups
  $(document).on('click', '.mt_primelink-field-group-header', function(){
    const $children = $(this).next('.mt_primelink-field-group-children');
    $(this).toggleClass('is-open');
    $children.slideToggle(120);
  });
}
// Debounce timer for live preview updates
let mt_primelinkLivePreviewTimer = null;

/**
 * Trigger live preview update with debounce
 * This function will update the preview automatically when mapping changes
 */
function triggerLivePreview(immediate = false) {
  // Clear existing timer
  if (mt_primelinkLivePreviewTimer) {
    clearTimeout(mt_primelinkLivePreviewTimer);
  }

  const updatePreview = () => {
    const index = window.mt_primelinkCurrentMapIndex || 0;
    loadMappingPreview(index, true); // Show loading indicator
  };

  if (immediate) {
    updatePreview();
  } else {
    // Debounce: wait 500ms after last change before updating
    mt_primelinkLivePreviewTimer = setTimeout(updatePreview, 500);
  }
}

async function loadMappingPreview(index, showLoading = false){
  const $fieldsWrap = $('#mt-primelink-fields');
  if(!$fieldsWrap.length) return;

  const importId = parseInt($fieldsWrap.data('import-id'), 10) || currentImportId();
  index = index || 0;

  // Don't make request if no valid import_id
  if(!importId || importId === 0){
    return;
  }

  // Show loading indicator if requested
  if(showLoading){
    const $preview = $('#mt-primelink-item-preview');
    if($preview.length){
      $preview.html('<div class="mt_primelink-live-preview-loading" style="padding:20px;text-align:center;color:#646970;"><span class="dashicons dashicons-update" style="animation:spin 1s linear infinite;display:inline-block;margin-right:8px;"></span>Updating preview...</div>');
    }
  }

  // keep current index for refresh-after-save
  window.mt_primelinkCurrentMapIndex = index;

  try {
    const filters = collectFilterRowsData();
    const forceRefresh = arguments[1] === true; // Allow force refresh parameter
    const res = await restPost('/import/mapping-preview', {
      import_id: importId,
      index: index,
      filters: filters,
      force_refresh: forceRefresh
    });

    // Left list (render only if changed to avoid flicker)
    const newFieldsRaw = res.fields || [];
    const newFields = newFieldsRaw.filter(f => {
      if(typeof f !== 'string') return false;
      const trimmed = f.trim();
      if(!trimmed) return false;
      if(trimmed.toLowerCase() === 'undefined') return false;
      return true;
    });
    let tree = res.fields_tree || null;

    // Sanitize tree nodes to avoid undefined labels
    const sanitizeTree = (nodes, parentFull) => {
      if(!Array.isArray(nodes)) return [];
      return nodes.map(n => {
        const rawName = typeof n.name === 'string' ? n.name : '';
        const rawFull = typeof n.full === 'string' ? n.full : '';
        const full = rawFull || (parentFull ? parentFull + '.' + rawName : rawName);
        const name = rawName || full || '';
        if(!full || full.toLowerCase() === 'undefined' || name.toLowerCase() === 'undefined'){
          return null;
        }
        const children = sanitizeTree(n.children || [], full);
        return { full, name, is_field: !!n.is_field, children };
      }).filter(n => n && n.full);
    };
    if(tree) tree = sanitizeTree(tree, '');

    const changed = true; // force rebuild to clear stale "undefined"
    if(changed || tree){
        let leftHtml = '';
        const hadPrev = mt_primelinkPrevFields.length > 0;

        function treeIsValid(nodes){
          if(!nodes || !nodes.length) return false;
          for(const n of nodes){
            if(!n || !(n.full || n.name)) return false;
            if(n.children && n.children.length && !treeIsValid(n.children)) return false;
          }
          return true;
        }

        function renderTree(nodes, depth, parentIsLast = false){
          if(!nodes || !nodes.length) return '';
          let html = '';
          nodes.forEach((node, index) => {
            const hasChildren = node.children && node.children.length;
          const isField = !!node.is_field;
          const full = node.full || node.name || '';
          if(!full) return;
          const indent = depth * 12;
          const isLast = index === nodes.length - 1;
          
          if(hasChildren){
            html += '<div class="mt_primelink-file-field mt_primelink-file-field--group" style="padding-left:'+indent+'px;position:relative;">';
            // Vertical connector line (only if not root level)
            if(depth > 0){
              html += '<div class="mt_primelink-tree-connector" style="position:absolute;left:'+(indent-6)+'px;top:0;bottom:0;width:1px;background:rgba(255,255,255,0.15);pointer-events:none;"></div>';
            }
            html += '  <div class="mt_primelink-field-group-header is-open" data-node="'+escapeHtml(full)+'" style="position:relative;padding-left:18px;">';
            // Tree branch indicator
            if(depth > 0){
              html += '<span class="mt_primelink-tree-branch" style="position:absolute;left:0;top:2px;color:rgba(255,255,255,0.5);font-size:14px;line-height:1;font-family:monospace;">' + (isLast ? 'â””' : 'â”œ') + '</span>';
            }
            html += escapeHtml(node.name || full);
            html += '</div>';
            html += '  <div class="mt_primelink-field-group-children" style="position:relative;padding-left:'+(depth > 0 ? '18' : '0')+'px;">';
            // If this node is also a field, render it inside the group (before children)
            if(isField){
              const isNew = hadPrev && mt_primelinkPrevFields.indexOf(node.full) === -1;
              const childrenCount = node.children.length;
              html += '<div class="mt_primelink-file-field'+(isNew?' is-new-field':'')+'" draggable="true" data-field="'+escapeHtml(full)+'" style="padding-left:0px;margin-bottom:4px;margin-top:2px;position:relative;">';
              if(childrenCount > 0){
                html += '<span class="mt_primelink-tree-branch" style="position:absolute;left:-18px;top:8px;color:rgba(255,255,255,0.5);font-size:14px;line-height:1;font-family:monospace;">â”œ</span>';
              }
              html += '<span class="mt_primelink-file-field__name">'+escapeHtml(full)+'</span>';
              html += '</div>';
            }
            // Render children recursively
            html += renderTree(node.children, depth+1, isLast);
            html += '  </div>';
            html += '</div>';
          } else {
            // Leaf node (no children) - render as regular field
            const isNew = hadPrev && mt_primelinkPrevFields.indexOf(node.full) === -1;
            html += '<div class="mt_primelink-file-field'+(isNew?' is-new-field':'')+'" draggable="true" data-field="'+escapeHtml(full)+'" style="padding-left:'+indent+'px;position:relative;">';
            // Tree branch indicator for leaf nodes
            if(depth > 0){
              html += '<span class="mt_primelink-tree-branch" style="position:absolute;left:'+(indent-18)+'px;top:8px;color:rgba(255,255,255,0.5);font-size:14px;line-height:1;font-family:monospace;">' + (isLast ? 'â””' : 'â”œ') + '</span>';
            }
            html += '<span class="mt_primelink-file-field__name">'+escapeHtml(full)+'</span>';
            html += '</div>';
          }
        });
        return html;
        }

        if(tree && tree.length && treeIsValid(tree)){
          leftHtml = renderTree(tree, 0);
        } else {
          newFields.forEach(f=>{
            const isNew = hadPrev && mt_primelinkPrevFields.indexOf(f) === -1;
            leftHtml += '<div class="mt_primelink-file-field'+(isNew?' is-new-field':'')+'" draggable="true" data-field="'+escapeHtml(f)+'">'
                   +  '<span class="mt_primelink-file-field__name">'+escapeHtml(f)+'</span>'
                   + '</div>';
        });
      }

      $fieldsWrap.html(leftHtml || '<div class="mt_primelink-empty">No fields found.</div>');
      filterFileFields();
      filterTargetSlots();
    }
    mt_primelinkMapFields = newFields;
    window.mt_primelinkMapFields = newFields; // Also set on window for AI mapping
    mt_primelinkPrevFields = newFields;
    renderSuggestions(newFields, res.item || {});

    // Right preview: mapped output (with separator/transforms/conditions/default)
    const item  = res.item  || {};
    const total = res.total || 0;
    const idx   = res.index || 0;

    const state = readCurrentMapping();
    const mapped = {};

    Object.keys(state.mapping || {}).forEach(target=>{
      mapped[target] = computeMappedValue(item, state.mapping[target]);
    });

  const mappedCustom = (state.customFields || []).map(cf=>{
    return {
      key: cf.key,
      value: computeMappedValue(item, cf)
    };
  });

    let vhtml = '';
    vhtml += '<div class="mt-preview-meta"><strong>Type:</strong> ' + escapeHtml(res.type) + '</div>';

    vhtml += '<div class="mt-preview-nav">';
    vhtml += '  <button type="button" class="button mt-map-prev" ' + (idx <= 0 ? 'disabled' : '') + ' data-index="' + Math.max(0, idx-1) + '">&lsaquo; Prev</button>';
    vhtml += '  <span class="mt-preview-counter">Row ' + (idx + 1) + ' / ' + total + '</span>';
    vhtml += '  <button type="button" class="button button-primary mt-map-next" ' + (idx >= total-1 ? 'disabled' : '') + ' data-index="' + Math.min(total-1, idx+1) + '">Next &rsaquo;</button>';
    vhtml += '</div>';

    vhtml += '<div class="mt-preview-vertical-wrap">';
    vhtml += '<table class="mt-preview-vertical">';
    vhtml += '<thead><tr><th>Target Field</th><th>Mapped Value</th></tr></thead><tbody>';

    // Add attributes to preview
    if(state.attributes && Array.isArray(state.attributes) && state.attributes.length > 0){
      state.attributes.forEach((attr, idx) => {
        if(attr && attr.name){
          // Show attribute even if source is not set yet
          if(!attr.source || !attr.source.trim()){
            vhtml += '<tr>';
            vhtml += '  <td class="mt-preview-field">' + escapeHtml(attr.name) + ' <span class="mt_primelink-map-expected">(Attribute)</span></td>';
            vhtml += '  <td class="mt-preview-value"><pre style="margin:0;white-space:pre-wrap;color:#999;">(not mapped)</pre></td>';
            vhtml += '</tr>';
            return;
          }
          // Get config from hidden input if available
          const $attrRow = $('.mt_primelink-attr-row').filter(function(){
            return $(this).find('.mt_primelink-attr-name').val().trim() === attr.name;
          }).first();
          let attrCfg = { source: attr.source };
          if($attrRow.length){
            const $configInput = $attrRow.find('.mt_primelink-attr-config');
            if($configInput.length && $configInput.val()){
              try {
                const parsedCfg = JSON.parse($configInput.val());
                attrCfg = { ...attrCfg, ...parsedCfg };
              } catch(e) {
                // Use default config
              }
            }
          }
          
          // Compute mapped value - use the same logic as regular fields
          const attrVal = computeMappedValue(item, attrCfg);
          
          // Format value for display
          let displayValue = '';
          if(Array.isArray(attrVal)){
            if(attrVal.length === 0){
              displayValue = '(empty)';
            } else if(attrVal.length === 1){
              displayValue = String(attrVal[0] || '(empty)');
            } else {
              displayValue = attrVal.map(v => "- " + String(v || '')).join("\n");
            }
          } else if(attrVal === undefined || attrVal === null || attrVal === ''){
            displayValue = '(empty)';
          } else {
            displayValue = String(attrVal);
          }

          vhtml += '<tr>';
          vhtml += '  <td class="mt-preview-field">' + escapeHtml(attr.name) + ' <span class="mt_primelink-map-expected">(Attribute)</span></td>';
          
          // Special handling for images
          if (Array.isArray(attrVal) && attrVal.length > 0) {
            const imageUrls = attrVal.filter(v => v && String(v).trim() && (String(v).startsWith('http') || String(v).startsWith('//')));
            if(imageUrls.length > 0){
              vhtml += '  <td class="mt-preview-value"><div class="mt-preview-gallery">';
              imageUrls.forEach(url => {
                vhtml += '<img src="' + escapeHtml(url) + '" alt="Preview" style="max-width:60px;max-height:60px;margin:2px;border-radius:4px;" />';
              });
              vhtml += '</div></td>';
            } else {
              vhtml += '  <td class="mt-preview-value"><pre style="margin:0;white-space:pre-wrap;">' + escapeHtml(displayValue) + '</pre></td>';
            }
          } else if (typeof attrVal === 'string' && attrVal.trim() && (attrVal.startsWith('http') || attrVal.startsWith('//'))) {
            vhtml += '  <td class="mt-preview-value"><img src="' + escapeHtml(attrVal) + '" alt="Preview" style="max-width:60px;max-height:60px;margin:2px;border-radius:4px;" /></td>';
          } else {
            // Always show value
            vhtml += '  <td class="mt-preview-value"><pre style="margin:0;white-space:pre-wrap;">' + escapeHtml(displayValue) + '</pre></td>';
          }
          vhtml += '</tr>';
        }
      });
    }

    Object.keys(mapped).forEach(target=>{
      let val = mapped[target];
      const rawVal = val;

      if(Array.isArray(val)){
        val = val.map(v => "- " + v).join("\n");
      } else {
        val = (val === undefined || val === null) ? "" : String(val);
      }

      const expected = expectedTypeForTarget(target);
      const actual   = detectValueType(rawVal);
      const warn     = validateType(expected, actual, val);

      vhtml += '<tr>';
      vhtml += '  <td class="mt-preview-field">' + escapeHtml(target) + (expected ? '<div class="mt_primelink-map-expected">'+escapeHtml(expected)+' expected</div>' : '') + '</td>';
      
      // Special handling for images fields - show as gallery
      if ((target === 'images' || target === 'featured_image') && (Array.isArray(rawVal) || (typeof rawVal === 'string' && rawVal.trim()))) {
        const imageUrls = Array.isArray(rawVal) ? rawVal : (rawVal ? [rawVal] : []);
        const validUrls = imageUrls.filter(url => url && String(url).trim() && (String(url).startsWith('http') || String(url).startsWith('//')));
        
        if (validUrls.length > 0) {
          let imgHtml = '<div class="mt_primelink-preview-images" style="display:flex;flex-wrap:wrap;gap:8px;margin-top:4px;">';
          validUrls.forEach(url => {
            const cleanUrl = String(url).trim();
            imgHtml += '<div class="mt_primelink-preview-image" style="position:relative;width:80px;height:80px;border:1px solid rgba(255,255,255,0.2);border-radius:4px;overflow:hidden;background:#f0f0f0;">';
            imgHtml += '<img src="' + escapeHtml(cleanUrl) + '" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display=\'none\';this.parentElement.innerHTML=\'<div style=\\\'padding:8px;text-align:center;font-size:10px;color:#999\\\'>Image<br/>Error</div>\';" />';
            imgHtml += '</div>';
          });
          imgHtml += '</div>';
          if (validUrls.length > 1) {
            imgHtml += '<small class="muted" style="display:block;margin-top:4px;">' + validUrls.length + ' images</small>';
          }
          vhtml += '  <td class="mt-preview-value' + (warn ? ' has-warning' : '') + '">' + imgHtml + (warn ? '<div class="mt_primelink-map-warning">'+escapeHtml(warn)+'</div>' : '') + '</td>';
        } else {
          vhtml += '  <td class="mt-preview-value' + (warn ? ' has-warning' : '') + '"><pre style="margin:0;white-space:pre-wrap;">' + escapeHtml(val) + '</pre>' + (warn ? '<div class="mt_primelink-map-warning">'+escapeHtml(warn)+'</div>' : '') + '</td>';
        }
      } else if(Array.isArray(rawVal)){
        const list = rawVal.map(v => '<span class="mt_primelink-chip">'+escapeHtml(String(v))+'</span>').join(' ');
        vhtml += '  <td class="mt-preview-value' + (warn ? ' has-warning' : '') + '"><div class="mt_primelink-chip-stack">'+list+'</div>' + (warn ? '<div class="mt_primelink-map-warning">'+escapeHtml(warn)+'</div>' : '') + '</td>';
      } else {
        // For status fields, always keep on same line
        const isStatusField = target === 'post_status' || target === 'status' || target.toLowerCase().includes('status');
        // For single-line values or status fields, use nowrap to keep on same line
        const isSingleLine = isStatusField || (!String(val).includes('\n') && String(val).length < 100);
        
        // For status fields, use span instead of pre to avoid block display
        if (isStatusField) {
          const spanStyle = 'margin:0;white-space:nowrap;display:inline;';
          vhtml += '  <td class="mt-preview-value mt-preview-status' + (warn ? ' has-warning' : '') + '"><span style="' + spanStyle + '">' + escapeHtml(val) + '</span>' + (warn ? '<div class="mt_primelink-map-warning">'+escapeHtml(warn)+'</div>' : '') + '</td>';
        } else {
          const preStyle = isSingleLine ? 'margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline;' : 'margin:0;white-space:pre-wrap;';
          vhtml += '  <td class="mt-preview-value' + (warn ? ' has-warning' : '') + '"><pre style="' + preStyle + '">' + escapeHtml(val) + '</pre>' + (warn ? '<div class="mt_primelink-map-warning">'+escapeHtml(warn)+'</div>' : '') + '</td>';
        }
      }
      vhtml += '</tr>';
    });

    if(mappedCustom.length){
      vhtml += '<tr><td colspan="2" style="font-weight:700;padding-top:10px;">Custom Fields</td></tr>';
      mappedCustom.forEach(cf=>{
        let val = cf.value;

        if(Array.isArray(val)){
        val = val.map(v => "- " + v).join("\\n");
      } else {
        val = (val === undefined || val === null) ? "" : String(val);
      }

        vhtml += '<tr>';
        // Check if custom field is a status field
        const isStatusField = cf.key && (cf.key.toLowerCase().includes('status') || cf.key.toLowerCase() === 'status');
        const statusClass = isStatusField ? ' mt-preview-status' : '';
        vhtml += '  <td class="mt-preview-field">' + escapeHtml(cf.key) + '</td>';
        if (isStatusField) {
          vhtml += '  <td class="mt-preview-value' + statusClass + '"><span style="margin:0;white-space:nowrap;display:inline;">' + escapeHtml(val) + '</span></td>';
        } else {
          vhtml += '  <td class="mt-preview-value' + statusClass + '"><pre style="margin:0;white-space:pre-wrap;">' + escapeHtml(val) + '</pre></td>';
        }
        vhtml += '</tr>';
      });
    }

    // Linked products preview
    const linkedKeys = ['upsell_refs','cross_sell_refs','grouped_children'];
    const linkedRows = linkedKeys
      .map(k => ({ key:k, val: mapped[k] }))
      .filter(r => r.val !== undefined && r.val !== null && String(r.val).trim() !== '');
    if(linkedRows.length){
      vhtml += '<tr><td colspan="2" style="font-weight:700;padding-top:10px;">Linked Products</td></tr>';
      linkedRows.forEach(r=>{
        vhtml += '<tr>';
        vhtml += '  <td class="mt-preview-field">'+escapeHtml(r.key)+'</td>';
        vhtml += '  <td class="mt-preview-value"><pre style="margin:0;white-space:pre-wrap;">'+escapeHtml(String(r.val))+'</pre></td>';
        vhtml += '</tr>';
      });
    }

    vhtml += '</tbody></table></div>';

    $('#mt-primelink-item-preview').html(vhtml);

    bindDnD();
    markUnmappedSlots();
    if(typeof syncSlotBadges === 'function'){
      syncSlotBadges();
    }

  } catch(err){
    const msg = err?.json?.message || err?.message || 'Unknown error';
    $fieldsWrap.html('Mapping preview failed.<br><pre>'+escapeHtml(msg)+'</pre>');
  }
}


function applyTransforms(value, cfg){
  let v = (value === undefined || value === null) ? '' : String(value);

  if(cfg?.transforms?.trim){
    v = v.trim();
  }
  if(cfg?.transforms?.lowercase){
    v = v.toLowerCase();
  }
  if(cfg?.transforms?.uppercase){
    v = v.toUpperCase();
  }
  if(cfg?.transforms?.capitalize){
    v = v.charAt(0).toUpperCase() + v.slice(1).toLowerCase();
  }
  if(cfg?.transforms?.title_case){
    v = v.replace(/\w\S*/g, (txt) => {
      return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });
  }
  if(cfg?.transforms?.normalize_html){
    v = normalizeHtml(v);
  }
  if(cfg?.transforms?.remove_accents){
    v = v.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  }
  if(cfg?.transforms?.slugify){
    v = v.toLowerCase()
      .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-+|-+$/g, '');
  }
  if(cfg?.transforms?.extract_numbers){
    v = v.replace(/[^\d.-]/g, '');
  }
  if(cfg?.transforms?.extract_text){
    v = v.replace(/[\d.-]/g, '');
  }
  if(cfg?.transforms?.url_encode){
    v = encodeURIComponent(v);
  }
  if(cfg?.transforms?.url_decode){
    try { v = decodeURIComponent(v); } catch(e) {}
  }
  
  // Number transforms
  if(cfg?.transforms?.round || cfg?.transforms?.ceil || cfg?.transforms?.floor){
    const num = parseFloat(v);
    if(!isNaN(num)){
      if(cfg.transforms.round){
        const decimals = parseInt(cfg.transforms.round_decimals) || 2;
        v = num.toFixed(decimals);
      } else if(cfg.transforms.ceil){
        v = Math.ceil(num).toString();
      } else if(cfg.transforms.floor){
        v = Math.floor(num).toString();
      }
    }
  }
  if(cfg?.transforms?.number_format && cfg.transforms.number_format !== ''){
    const num = parseFloat(v);
    if(!isNaN(num)){
      const parts = num.toFixed(2).split('.');
      if(cfg.transforms.number_format === 'comma'){
        v = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + parts[1];
      } else if(cfg.transforms.number_format === 'dot'){
        v = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ',' + parts[1];
      } else if(cfg.transforms.number_format === 'space'){
        v = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + '.' + parts[1];
      }
    }
  }
  
  // String operations
  if(cfg?.transforms?.substring_start !== undefined && cfg.transforms.substring_start !== ''){
    const start = parseInt(cfg.transforms.substring_start) || 0;
    const length = cfg.transforms.substring_length ? parseInt(cfg.transforms.substring_length) : undefined;
    v = v.substring(start, length !== undefined ? start + length : undefined);
  }
  if(cfg?.transforms?.pad_side && cfg.transforms.pad_length){
    const length = parseInt(cfg.transforms.pad_length) || 0;
    const char = cfg.transforms.pad_char || ' ';
    if(cfg.transforms.pad_side === 'left'){
      v = v.padStart(length, char);
    } else if(cfg.transforms.pad_side === 'right'){
      v = v.padEnd(length, char);
    }
  }
  
  // Date transforms
  if(cfg?.transforms?.date_format && cfg.transforms.date_output){
    try {
      // Try to parse date with input format
      const date = parseDate(v, cfg.transforms.date_format);
      if(date && !isNaN(date.getTime())){
        v = formatDate(date, cfg.transforms.date_output);
      }
    } catch(e) {}
  }
  
  // Regex replace
  if(cfg?.transforms?.regex_pattern && cfg.transforms.regex_pattern !== ''){
    try {
      const pattern = new RegExp(cfg.transforms.regex_pattern, 'g');
      const replacement = cfg.transforms.regex_replacement || '';
      v = v.replace(pattern, replacement);
    } catch(e) {}
  }
  
  // Replace rules (after other transforms)
  if(Array.isArray(cfg?.transforms?.replace)){
    cfg.transforms.replace.forEach(r=>{
      if(!r || r.from === undefined) return;
      const from = String(r.from);
      const to   = String(r.to ?? '');
      if(from === '') return;
      v = v.split(from).join(to);
    });
  }

  return v;
}

// Helper functions for date parsing/formatting
function parseDate(dateString, format){
  if(!dateString || !format) return null;
  
  // Simple date parsing - supports common formats
  const formatMap = {
    'Y-m-d': (s) => {
      const parts = s.split('-');
      return new Date(parseInt(parts[0]), parseInt(parts[1])-1, parseInt(parts[2]));
    },
    'd/m/Y': (s) => {
      const parts = s.split('/');
      return new Date(parseInt(parts[2]), parseInt(parts[1])-1, parseInt(parts[0]));
    },
    'm/d/Y': (s) => {
      const parts = s.split('/');
      return new Date(parseInt(parts[2]), parseInt(parts[1])-1, parseInt(parts[0]));
    },
    'Y-m-d H:i:s': (s) => {
      const [date, time] = s.split(' ');
      const [y, m, d] = date.split('-');
      const [h, i, sec] = time.split(':');
      return new Date(parseInt(y), parseInt(m)-1, parseInt(d), parseInt(h), parseInt(i), parseInt(sec));
    }
  };
  
  if(formatMap[format]){
    try {
      return formatMap[format](dateString);
    } catch(e) {
      return null;
    }
  }
  
  // Fallback to Date.parse
  const parsed = Date.parse(dateString);
  return isNaN(parsed) ? null : new Date(parsed);
}

function formatDate(date, format){
  if(!date || !format) return '';
  
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');
  
  return format
    .replace('Y', year)
    .replace('m', month)
    .replace('d', day)
    .replace('H', hours)
    .replace('i', minutes)
    .replace('s', seconds);
}

function adjustPrice(value, adj){
  const num = parseFloat(value);
  const amt = parseFloat(adj?.amount);
  if(isNaN(num) || isNaN(amt)) return value;
  let out = num;
  switch(adj?.type){
    case 'percent_plus':
      out = num + num * (amt/100);
      break;
    case 'percent_minus':
      out = num - num * (amt/100);
      break;
    case 'fixed_plus':
      out = num + amt;
      break;
    case 'fixed_minus':
      out = num - amt;
      break;
    default:
      return value;
  }
  return out;
}

function applySeparator(value, sep){
  if(!sep) return value;
  const parts = String(value).split(sep).map(s=>s.trim()).filter(Boolean);
  return parts;
}

function normalizeHtml(str){
  if(!str) return '';
  let cleaned = str.replace(/<[^>]+>/g, ' ');
  // decode entities using textarea trick
  const div = document.createElement('textarea');
  div.innerHTML = cleaned;
  cleaned = div.value;
  cleaned = cleaned.replace(/\s+/g, ' ').trim();
  return cleaned;
}

function checkConditions(item, conditions){
  if(!Array.isArray(conditions) || !conditions.length) return true;

  return conditions.every(c=>{
    const left = item[c.field] !== undefined ? String(item[c.field]) : '';
    const right = String(c.value ?? '');

    switch(c.op){
      case '=': return left === right;
      case '!=': return left !== right;
      case '>': return parseFloat(left) > parseFloat(right);
      case '<': return parseFloat(left) < parseFloat(right);
      case 'contains': return left.includes(right);
      default: return true;
    }
  });
}

function expectedTypeForTarget(target){
  const t = (target || '').toLowerCase();
  if(/price|cost|amount|total|qty|quantity|stock|weight|length|width|height|tax/.test(t)){
    return 'number';
  }
  if(/date|time/.test(t)){
    return 'date';
  }
  if(/url|link|image|img/.test(t)){
    return 'url';
  }
  return '';
}

function detectValueType(val){
  if(Array.isArray(val) && val.length){
    return detectValueType(val[0]);
  }
  const v = (val === undefined || val === null) ? '' : String(val).trim();
  if(v === '') return 'empty';
  if(/^https?:\/\//i.test(v)) return 'url';
  const num = parseFloat(v);
  if(!isNaN(num) && isFinite(num)) return 'number';
  const ts = Date.parse(v);
  if(!isNaN(ts)) return 'date';
  return 'text';
}

function arraysEqual(a, b){
  if(!Array.isArray(a) || !Array.isArray(b)) return false;
  if(a.length !== b.length) return false;
  for(let i=0;i<a.length;i++){
    if(a[i] !== b[i]) return false;
  }
  return true;
}

function validateType(expected, actual, displayVal){
  if(!expected) return '';
  if(actual === 'empty') return '';
  if(expected === 'number' && actual !== 'number') return 'Expected number';
  if(expected === 'date' && actual !== 'date') return 'Expected date';
  if(expected === 'url' && actual !== 'url') return 'Expected URL';
  return '';
}

function validateCriticalMapping(){
  const status = calcRequiredStatus();
  renderRequiredChecklist(status);
  return status.ok;
}

function calcRequiredStatus(){
  // Skip required field validation for orders
  if(document.body.classList.contains('mt_primelink-import-orders')){
    const state = readCurrentMapping();
    const m = state.mapping || {};
    const hasOrderId = !!(m['order_id'] && m['order_id'].source);
    const hasCustomerEmail = !!(m['customer_email'] && m['customer_email'].source);
    return {
      hasTitle: true,
      hasSku: true,
      hasPrice: true,
      ok: hasOrderId || hasCustomerEmail // At least one identifier for orders
    };
  }
  // Required field validation for customers
  if(document.body.classList.contains('mt_primelink-import-customers')){
    const state = readCurrentMapping();
    const m = state.mapping || {};
    const hasEmail = !!(m['email'] && m['email'].source);
    return {
      hasTitle: true,
      hasSku: true,
      hasPrice: true,
      ok: hasEmail // Email is required for customers
    };
  }
  // Required field validation for coupons
  if(document.body.classList.contains('mt_primelink-import-coupons')){
    const state = readCurrentMapping();
    const m = state.mapping || {};
    const hasCode = !!(m['code'] && m['code'].source);
    return {
      hasTitle: true,
      hasSku: true,
      hasPrice: true,
      ok: hasCode // Code is required for coupons
    };
  }
  const state = readCurrentMapping();
  const m = state.mapping || {};
  const hasTitle = !!(m['post_title'] && m['post_title'].source);
  const hasSku   = !!(m['sku'] && m['sku'].source);
  const hasPrice = !!(
    (m['price'] && m['price'].source) ||
    (m['regular_price'] && m['regular_price'].source) ||
    (m['sale_price'] && m['sale_price'].source)
  );
  return {
    hasTitle,
    hasSku,
    hasPrice,
    ok: hasPrice && (hasTitle || hasSku)
  };
}

function showNextTooltip(msg){
  let $tip = $('#mt_primelink-next-tooltip');
  if(!$tip.length){
    const $btn = $('#mt-primelink-next');
    $tip = $('<div id="mt_primelink-next-tooltip" class="mt_primelink-next-tooltip"></div>');
    $btn.after($tip);
  }
  $tip.text(msg).fadeIn(120);
  clearTimeout(window.mt_primelinkNextTipTimer);
  window.mt_primelinkNextTipTimer = setTimeout(()=> $tip.fadeOut(200), 3000);
}

// Build final mapped value for a target slot
function computeMappedValue(item, slotCfg){
  if(!slotCfg) return '';
  if(slotCfg.constant !== undefined) return slotCfg.constant;
  if(!slotCfg.source) return '';

  let raw = item[slotCfg.source];
  // Fallbacks for list-style keys
  if(raw === undefined || raw === null || raw === ''){
    const s = slotCfg.source;
    if(s.endsWith('[]') && item[s.slice(0, -2)] !== undefined){
      raw = item[s.slice(0, -2)];
    } else if(item[s + '.0'] !== undefined){
      raw = item[s + '.0'];
    }
  }
  raw = raw ?? '';
  if(!checkConditions(item, slotCfg.conditions)) return '';

  const rawValues = [];
  rawValues.push(raw);
  if(Array.isArray(slotCfg.extra_sources)){
    slotCfg.extra_sources.forEach(f=>{
      if(f){
        rawValues.push(item[f] ?? '');
      }
    });
  }

  let results = [];
  rawValues.forEach(rv=>{
    let v = applyTransforms(rv, slotCfg);

    if((v === '' || v === null) && slotCfg.default){
      v = slotCfg.default;
    }

    // Price adjustment for price targets
    if(slotCfg.price_adjust){
      v = adjustPrice(v, slotCfg.price_adjust);
    }

    if(slotCfg.separator){
      const arr = applySeparator(v, slotCfg.separator);
      if(Array.isArray(arr)){
        arr.forEach(a=> results.push(a));
        return;
      }
    }
    results.push(v);
  });

  const mode = slotCfg.template_mode || 'before';

  results = results
    .map(val => {
      let v = (val === undefined || val === null) ? '' : String(val);

      if(slotCfg.template){
        if(slotCfg.template.includes('{value}')){
          v = slotCfg.template.replace(/\{value\}/g, v);
        } else {
          if(mode === 'before'){
            v = slotCfg.template + v;
          } else if(mode === 'after'){
            v = v + slotCfg.template;
          } else if(mode === 'replace'){
            v = slotCfg.template;
          }
        }
      }

      if(slotCfg.prefix){
        v = slotCfg.prefix + v;
      }
      if(slotCfg.suffix){
        v = v + slotCfg.suffix;
      }
      return v;
    })
    .filter(v => v !== '');

  if(results.length > 1){
    return results;
  }
  return results[0] ?? '';
}

function renderRequiredChecklist(status){
  status = status || calcRequiredStatus();
  const $list = $('#mt_primelink-required-list');
  const $stat = $('#mt_primelink-required-status');
  if(!$list.length) return status.ok;
  
  // For orders, show order-specific required fields
  if(document.body.classList.contains('mt_primelink-import-orders')){
    const m = collectMappingState().mapping || {};
    const hasOrderId = Object.keys(m).some(k => ['order_id'].includes(k.toLowerCase()) && m[k].source);
    const hasCustomerEmail = Object.keys(m).some(k => ['customer_email'].includes(k.toLowerCase()) && m[k].source);
    const rows = [
      { key: 'order_id', label: 'Order ID or Customer Email', ok: (hasOrderId || hasCustomerEmail), detail: hasOrderId ? 'Order ID mapped' : (hasCustomerEmail ? 'Customer Email mapped' : 'Missing both') },
    ];
    $list.empty();
    rows.forEach(r=>{
      const pill = $('<li class="mt_primelink-helper-pill"></li>').addClass(r.ok ? 'ok' : 'missing');
      pill.append('<span>'+escapeHtml(r.label)+'</span>');
      pill.append('<span class="muted">'+escapeHtml(r.detail)+'</span>');
      $list.append(pill);
    });
    if($stat.length){
      $stat.text(status.ok ? 'Ready to run' : 'Missing required fields');
      $stat.toggleClass('error', !status.ok);
    }
    return status.ok;
  }
  // For customers, show customer-specific required fields
  if(document.body.classList.contains('mt_primelink-import-customers')){
    const m = collectMappingState().mapping || {};
    const hasEmail = Object.keys(m).some(k => ['email'].includes(k.toLowerCase()) && m[k].source);
    const rows = [
      { key: 'email', label: 'Email', ok: hasEmail, detail: hasEmail ? 'Email mapped' : 'Missing' },
    ];
    $list.empty();
    rows.forEach(r=>{
      const pill = $('<li class="mt_primelink-helper-pill"></li>').addClass(r.ok ? 'ok' : 'missing');
      pill.append('<span>'+escapeHtml(r.label)+'</span>');
      pill.append('<span class="muted">'+escapeHtml(r.detail)+'</span>');
      $list.append(pill);
    });
    if($stat.length){
      $stat.text(status.ok ? 'Ready to run' : 'Missing required fields');
      $stat.toggleClass('error', !status.ok);
    }
    return status.ok;
  }
  
  // For products, show product-specific required fields
  const rows = [
    { key: 'title', label: 'Title or SKU', ok: (status.hasTitle || status.hasSku), detail: status.hasTitle ? 'Title mapped' : (status.hasSku ? 'SKU mapped' : 'Missing both') },
    { key: 'price', label: 'Price', ok: status.hasPrice, detail: status.hasPrice ? 'Price mapped' : 'Missing price (regular/sale/price)' },
  ];
  $list.empty();
  rows.forEach(r=>{
    const pill = $('<li class="mt_primelink-helper-pill"></li>').addClass(r.ok ? 'ok' : 'missing');
    pill.append('<span>'+escapeHtml(r.label)+'</span>');
    pill.append('<span class="muted">'+escapeHtml(r.detail)+'</span>');
    $list.append(pill);
  });
  if($stat.length){
    $stat.text(status.ok ? 'Ready to run' : 'Missing required fields');
    $stat.toggleClass('error', !status.ok);
  }
  return status.ok;
}

function buildSuggestions(fields, item){
  const list = Array.isArray(fields) ? fields : [];
  const lower = list.map(f => ({ name: f, key: f.toLowerCase() }));
  const getVal = (f) => item && Object.prototype.hasOwnProperty.call(item, f) ? item[f] : '';

  const pick = (keywords = [], type) => {
    let best = null;
    lower.forEach(entry => {
      let score = 0;
      keywords.forEach(kw => { if(entry.key.indexOf(kw) !== -1) score += 3; });
      if(type){
        const t = detectValueType(getVal(entry.name));
        if(type === t) score += 2;
      }
      if(score > (best?.score || 0)){
        best = { field: entry.name, score };
      }
    });
    return best ? best.field : '';
  };

  return [
    { target: 'post_title', source: pick(['title','name','productname','nume']), label: 'Title' },
    { target: 'post_content', source: pick(['description','descr','content','longdesc']), label: 'Description' },
    { target: 'sku', source: pick(['sku','code','cod','productcode','item'], 'text'), label: 'SKU' },
    { target: 'price', source: pick(['price','pret','amount','cost','value'], 'number'), label: 'Price' },
    { target: 'regular_price', source: pick(['regular_price','list','standard','pretstandard'], 'number'), label: 'Regular price' },
    { target: 'sale_price', source: pick(['sale','promo','discount'], 'number'), label: 'Sale price' },
    { target: 'avatar', source: pick(['image','img','avatar','thumb','thumbnail','main'], 'url'), label: 'Main image' }
  ].filter(s => s.source);
}

function renderSuggestions(fields, item){
  const $box = $('#mt_primelink-suggestion-list');
  if(!$box.length) return;
  const sugg = buildSuggestions(fields, item);
  if(!sugg.length){
    $box.html('<div class="mt_primelink-helper-pill missing">No suggestions found.</div>');
    return;
  }
  $box.empty();
  sugg.forEach(s => {
    const pill = $('<div class="mt_primelink-helper-pill"></div>');
    pill.append('<span>'+escapeHtml(s.label)+':</span>');
    pill.append('<code>'+escapeHtml(s.source)+'</code>');
    const btn = $('<button type="button" class="button button-small mt_primelink-suggestion-action">Apply</button>');
    btn.data('target', s.target).data('source', s.source);
    pill.append(btn);
    const clr = $('<a href="#" class="mt_primelink-suggestion-clear" aria-label="Clear mapping">&times;</a>');
    clr.data('target', s.target);
    pill.append(clr);
    $box.append(pill);
  });
}

function applyMapping(target, source){
  const $slot = $('.mt_primelink-map-slot[data-target="'+target+'"]');
  if(!$slot.length) return false;
  $slot.find('.mt_primelink-map-input').val(source);
  $slot.find('.mt_primelink-map-drop').html(buildChip(source));
  $slot.find('.mt_primelink-map-edit').prop('disabled', false).addClass('is-visible');
  setSlotBadge($slot, '');
  return true;
}

function buildChip(source){
  const safe = (source === undefined || source === null || source === '') ? '(unknown)' : source;
  return '<span class="mt_primelink-chip" data-source="'+escapeHtml(safe)+'">'+escapeHtml(safe)+' <a href="#" class="mt_primelink-chip-remove" aria-label="Remove">x</a><span class="mt_primelink-chip-edit-label">edit</span></span>';
}

// Read current mapping state from DOM into normalized object
function readCurrentMapping(){
  const mapping = {};

  $('.mt_primelink-map-slot').each(function(){
    const target = $(this).data('target');
    const source = ($(this).find('.mt_primelink-map-input').val() || '').trim();
    let cfg = {};
    try {
      cfg = JSON.parse($(this).find('.mt_primelink-map-config').val() || '{}');
    } catch(e){ cfg = {}; }

    // Only save to mapping if there's actually a source mapped
    // This prevents saving empty mappings that will fail to load later
    if(target && source){
      mapping[target] = { source, ...(cfg || {}) };
    }
  });

  // Attributes
  const attributes = [];
  $('.mt_primelink-attr-row').each(function(){
    const $row = $(this);
    const name = ($row.find('.mt_primelink-attr-name').val() || '').trim();
    const $sourceInput = $row.find('.mt_primelink-attr-source');
    const source = ($sourceInput.val() || '').trim();
    const variation = $row.find('.mt_primelink-attr-variation-toggle').is(':checked');
    const visible   = $row.find('.mt_primelink-attr-visible-toggle').is(':checked');
    
    if(name){
      mapping['attribute:' + name] = { source, variation, visible };
      attributes.push({ name, source, variation, visible });
    }
  });

  // Custom fields
  const customFields = [];
  $('.mt_primelink-custom-field').each(function(){
    const key = $(this).find('.mt_primelink-custom-key').val() || '';
    const field = $(this).find('.mt_primelink-custom-val').val() || '';
    let cfg = {};
    try {
      cfg = JSON.parse($(this).find('.mt_primelink-map-config').val() || '{}');
    } catch(e){ cfg = {}; }
    if(key){
      customFields.push({ key, source: field, ...(cfg || {}) });
    }
  });

  // ACF fields (treated as custom meta; kept separate in state as acf_fields)
  const acfFields = [];
  $('.mt_primelink-acf-field').each(function(){
    const $row = $(this);
    const label = $row.find('.mt_primelink-acf-label').val() || '';
    const key = $row.find('.mt_primelink-acf-key').val() || '';
    const $slot = $row.find('.mt_primelink-map-slot');
    const source = $slot.find('.mt_primelink-map-input').val() || '';
    let cfg = {};
    try {
      cfg = JSON.parse($row.find('.mt_primelink-map-config').val() || '{}');
    } catch(e){ cfg = {}; }
    if(key){
      acfFields.push({ label, key, source, ...(cfg || {}) });
      // Also push into customFields so backend treats them as meta
      customFields.push({ key, source, ...(cfg || {}) });
    }
  });

  return {
    mapping,
    attributes,
    customFields,
    acfFields,
    createVariations: $('#mt_primelink-create-variations').is(':checked'),
    productType: mt_primelinkSelectedProductType
  };
}

function collectMappingState(){
  const state = readCurrentMapping();
  return {
    mapping: state.mapping,
    attributes: state.attributes,
    custom_fields: state.customFields,
    acf_fields: state.acfFields,
    create_variations: state.createVariations,
    product_type: state.productType,
    row_filters: collectFilterRowsData(),
    custom_aliases: collectAliasState(),
    other_options: collectOtherOptions()
  };
}

function setSaveStatus(state, message, withRetry){
  const $status = $('#mt_primelink-save-status');
  if(!$status.length) return;

  $status.removeClass('is-saving is-saved is-error');
  const now = new Date();
  const stamp = now.toLocaleTimeString();

  if(state === 'saving'){
    $status.addClass('is-saving').html('<span class="mt_primelink-save-spinner" aria-hidden="true"></span><span>'+escapeHtml(message || 'Saving...')+'</span>');
    $status.attr('data-tooltip', '');
    return;
  }

  if(state === 'saved'){
    mt_primelinkStep5LastSaveFailed = false;
    $status.addClass('is-saved').text(message || 'Saved');
    $status.attr('data-tooltip', 'Saved at ' + stamp);
    return;
  }

  if(state === 'error'){
    mt_primelinkStep5LastSaveFailed = true;
    $status.addClass('is-error');
    if(withRetry){
      const label = message || 'Save failed';
      $status.html(label + ' <button type="button" class="button-link" id="mt_primelink-save-retry">Retry</button>');
    } else {
      $status.text(message || 'Save failed');
    }
    $status.attr('data-tooltip', '');
    return;
  }

  $status.text('');
  $status.attr('data-tooltip', '');
}

function pushMapHistory(){ /* undo/redo disabled */ }

// Manage recent existing paths (local storage, keep last 3) and server files
function addExistingRecent(path){
  if(!path) return;
  try{
    const key = 'mt_primelink-existing-paths';
    let arr = JSON.parse(localStorage.getItem(key) || '[]');
    arr = arr.filter(p => p !== path);
    arr.unshift(path);
    arr = arr.slice(0,3);
    localStorage.setItem(key, JSON.stringify(arr));
    populateExistingRecent(window._mt_primelinkExistingServerList || []);
  }catch(e){}
}
function populateExistingRecent(serverPaths){
  const $sel = $('#mt_primelink-existing-recent');
  if(!$sel.length) return;
  let arr = [];
  try{
    arr = JSON.parse(localStorage.getItem('mt_primelink-existing-paths') || '[]');
  }catch(e){ arr = []; }
  window._mt_primelinkExistingServerList = Array.isArray(serverPaths) ? serverPaths : (window._mt_primelinkExistingServerList || []);

  let html = '<option value="">Recent...</option>';
  arr.forEach(p=>{
    html += '<option value="'+escapeHtml(p)+'">'+escapeHtml(p)+'</option>';
  });
  if(window._mt_primelinkExistingServerList.length){
    html += '<optgroup label="Uploads">';
    window._mt_primelinkExistingServerList.forEach(item=>{
      const label = (item.path || item).toString();
      const extra = item.size ? ' ('+Math.round(item.size/1024)+' KB, '+(new Date(item.mtime*1000)).toLocaleString()+')' : '';
      html += '<option value="'+escapeHtml(label)+'">'+escapeHtml(label+extra)+'</option>';
    });
    html += '</optgroup>';
  }
  $sel.html(html);
}

async function fetchExistingUploads(){
  try{
    const res = await restGet('/import/uploads?ts=' + Date.now());
    const list = res.files || [];
    populateExistingRecent(list);
  }catch(e){
    populateExistingRecent([]);
  }
}

// Persist step 5 mapping to draft (debounced)
function saveStep5Mapping(immediate){
  if(mt_primelinkStep5SaveTimer){
    clearTimeout(mt_primelinkStep5SaveTimer);
  }

  setSaveStatus('saving');

  const runner = async () => {
    let importId = currentImportId() || parseInt($('.mt_primelink-import-step-5').data('import-id'), 10) || 0;
    if(!importId){
      try{
        const draft = await restPost('/import/create-draft', {});
        importId = draft.import_id || 0;
        if(importId){
          setImportIdEverywhere(importId);
          $('.mt_primelink-import-step-5').attr('data-import-id', importId);
        }
      }catch(e){
        setSaveStatus('error','Import ID missing', true);
        return;
      }
    }
    const payload = collectMappingState();
    try{
      await restPost('/import/save-step', {
        import_id: importId,
        step: 5,
        data: payload
      });
      setSaveStatus('saved');
    } catch(err){
      setSaveStatus('error', err?.json?.message || 'Save failed', true);
    }
  };

  if(immediate){
    return runner();
  }

  mt_primelinkStep5SaveTimer = setTimeout(runner, 100);
}

function renderAttributeRow(name, source, variation){
  name = name || '';
  source = source || '';
  const dropHtml = source
    ? buildChip(source)
    : '<span class="mt_primelink-muted">Drop field here</span>';
  const unmappedClass = source ? '' : ' unmapped';
  return ''
    + '<div class="mt_primelink-attr-row">'
    + '  <div class="mt_primelink-attr-row__header">'
    + '    <input type="text" class="mt_primelink-attr-name" placeholder="Attribute name (e.g. Color)" value="'+escapeHtml(name)+'" />'
    + '    <label class="mt_primelink-attr-variation"><input type="checkbox" class="mt_primelink-attr-visible-toggle" '+(variation?'checked':'')+' /> Visible</label>'
    + '    <label class="mt_primelink-attr-variation"><input type="checkbox" class="mt_primelink-attr-variation-toggle" '+(variation?'checked':'')+' /> Use for variations</label>'
    + '    <button type="button" class="button-link-delete mt_primelink-attr-remove">Remove</button>'
    + '  </div>'
    + '  <div class="mt_primelink-map-slot'+unmappedClass+'">'
    + '    <div class="mt_primelink-map-label">'
    + '      <span class="mt_primelink-map-label__text">'+(name || 'Attribute Values')+'</span>'
    + '      <button type="button" class="button button-small mt_primelink-map-edit mt_primelink-map-edit--inline mt_primelink-attr-edit" '+(source ? '' : 'disabled')+'>Edit</button>'
    + '    </div>'
    + '    <div class="mt_primelink-map-drop mt-map-slot__drop">'+dropHtml+'</div>'
    + '  </div>'
    + '  <input type="hidden" class="mt_primelink-attr-source" value="'+escapeHtml(source)+'" />'
    + '  <input type="hidden" class="mt_primelink-attr-config" value="" />'
    + '</div>';
}


// Step 5 navigator
$(document).on('click', '.mt-map-prev, .mt-map-next', function(e){
  e.preventDefault();
  const idx = parseInt($(this).data('index'), 10) || 0;
  loadMappingPreview(idx);
});

// Field search (Step 5 left list)
$(document).on('input', '#mt_primelink-field-search', function(){
  filterFileFields();
});

// Refresh fields button (reload from CSV)
$(document).on('click', '#mt_primelink-refresh-fields', function(){
  const $btn = $(this);
  const $icon = $btn.find('.dashicons');
  
  // Show loading state
  $icon.addClass('spin');
  $btn.prop('disabled', true);
  
  // Clear any cached fields and reload
  mt_primelinkPrevFields = [];
  window.mt_primelinkMapFields = [];
  
  // Force reload preview with cache bust
  const importId = parseInt($('#mt-primelink-fields').data('import-id'), 10) || currentImportId();
  const currentIndex = window.mt_primelinkCurrentMapIndex || 0;
  
  // Reload preview with force refresh (this will fetch fresh fields from CSV)
  loadMappingPreview(currentIndex, true).then(() => {
    $icon.removeClass('spin');
    $btn.prop('disabled', false);
  }).catch(() => {
    $icon.removeClass('spin');
    $btn.prop('disabled', false);
  });
});

// Target search (right side)
$(document).on('input', '#mt_primelink-target-search', function(){
  filterTargetSlots();
});
// Custom fields add/remove
// Add gallery slot
let gallerySlotCounter = 1;

function initGallerySlotsCounter(){
  // Find highest counter from existing slots
  let max = 0;
  $('#mt_primelink-gallery-slots .mt_primelink-map-slot[data-target^="images_"]').each(function(){
    const target = $(this).data('target');
    const match = target.match(/^images_(\d+)$/);
    if(match){
      const num = parseInt(match[1], 10);
      if(num > max) max = num;
    }
  });
  gallerySlotCounter = max + 1;
}

function renderGallerySlot(slotId, index, source, config){
  const $slot = $(`
    <div class="mt_primelink-map-slot" data-target="${slotId}">
      <div class="mt_primelink-map-label">
        <span class="mt_primelink-map-label__text">Images (Gallery) ${index > 0 ? index : ''}</span>
        <button type="button" class="button button-small mt_primelink-map-edit mt_primelink-map-edit--inline" ${source ? '' : 'disabled'}>Edit</button>
        ${index > 0 ? '<button type="button" class="button button-small button-link-delete mt_primelink-remove-gallery-slot" style="margin-left:4px;">Remove</button>' : ''}
      </div>
      <div class="mt_primelink-map-drop mt-map-slot__drop">
        ${source ? `<span class="mt_primelink-mapped-field">${escapeHtml(source)}</span>` : '<span class="mt_primelink-muted">Drop field here</span>'}
      </div>
      <input type="hidden" class="mt_primelink-map-input" name="mapping[${slotId}][source]" value="${escapeHtml(source || '')}" />
      <input type="hidden" class="mt_primelink-map-config" name="mapping[${slotId}][config]" value="${escapeHtml(config || '')}" />
    </div>
  `);
  
  if(source){
    const cfg = config ? JSON.parse(config) : {};
    setSlotConfig($slot, cfg);
    setSlotBadge($slot, cfg.source_mode || 'manual');
    $slot.find('.mt_primelink-map-edit').addClass('is-visible').prop('disabled', false);
  }
  
  return $slot;
}

$(document).on('click', '#mt_primelink-add-gallery-slot', function(){
  const slotId = 'images_' + gallerySlotCounter;
  const index = gallerySlotCounter;
  gallerySlotCounter++;
  
  const $slot = renderGallerySlot(slotId, index, '', '');
  $('#mt_primelink-gallery-slots').append($slot);
  bindDnD();
  saveStep5Mapping();
});

$(document).on('click', '.mt_primelink-remove-gallery-slot', function(){
  if(!confirm('Remove this gallery field?')) return;
  $(this).closest('.mt_primelink-map-slot').remove();
  saveStep5Mapping();
});

// Initialize counter and load existing gallery slots on page load
$(document).ready(function(){
  initGallerySlotsCounter();
  loadExistingGallerySlots();
});

function loadExistingGallerySlots(){
  // Wait for mapping to be loaded
  setTimeout(function(){
    const mapping = collectStep5Mapping();
    if(!mapping) return;
    
    // Find all images_* slots in mapping
    const gallerySlots = [];
    Object.keys(mapping).forEach(target => {
      if(target === 'images' || (target.startsWith('images_') && target !== 'images')){
        const match = target.match(/^images_(\d+)$/);
        const index = match ? parseInt(match[1], 10) : 0;
        gallerySlots.push({ target, index, cfg: mapping[target] });
      }
    });
    
    // Sort by index
    gallerySlots.sort((a, b) => a.index - b.index);
    
    // Remove the default "images" slot if it exists and we have additional slots
    if(gallerySlots.length > 1){
      const defaultSlot = gallerySlots.find(s => s.target === 'images');
      if(defaultSlot){
        const $defaultSlot = $('#mt_primelink-gallery-slots .mt_primelink-map-slot[data-target="images"]');
        if($defaultSlot.length){
          // Keep it, just update label
          $defaultSlot.find('.mt_primelink-map-label__text').text('Images (Gallery)');
        }
      }
    }
    
    // Add additional slots
    gallerySlots.forEach(slot => {
      if(slot.target !== 'images'){
        const $existing = $('#mt_primelink-gallery-slots .mt_primelink-map-slot[data-target="' + slot.target + '"]');
        if(!$existing.length){
          const source = slot.cfg?.source || '';
          const config = slot.cfg ? JSON.stringify(slot.cfg) : '';
          const $slot = renderGallerySlot(slot.target, slot.index, source, config);
          $('#mt_primelink-gallery-slots').append($slot);
          bindDnD();
        }
      }
    });
    
    initGallerySlotsCounter();
  }, 500);
}

function collectStep5Mapping(){
  const mapping = {};
  $('.mt_primelink-map-slot').each(function(){
    const target = $(this).data('target');
    const source = $(this).find('.mt_primelink-map-input').val() || '';
    let cfg = {};
    try {
      cfg = JSON.parse($(this).find('.mt_primelink-map-config').val() || '{}');
    } catch(e){ cfg = {}; }

    if(target && source){
      mapping[target] = { source, ...(cfg || {}) };
    }
  });
  return mapping;
}

$(document).on('click', '#mt_primelink-add-custom-field', function(){
  const i = $('.mt_primelink-custom-field').length;
  const html =
    '<div class="mt_primelink-custom-field" data-index="'+i+'">' +
      '<input type="text" class="mt_primelink-custom-key" placeholder="Meta key" />' +
      '<div class="mt_primelink-map-slot unmapped">' +
        '<div class="mt_primelink-map-drop mt-map-slot__drop"><span class="mt_primelink-muted">Drop field here</span></div>' +
      '</div>' +
      '<input type="hidden" class="mt_primelink-custom-val" value="" />' +
      '<input type="hidden" class="mt_primelink-map-config" value="" />' +
      '<button type="button" class="button-link-delete mt_primelink-custom-remove">Remove</button>' +
    '</div>';
  $('#mt_primelink-custom-fields-wrap').append(html);
  bindDnD();
});

// ACF dynamic fields: label + meta key + drag slot
$(document).on('click', '#mt_primelink-add-acf-field', function(){
  const i = $('.mt_primelink-acf-field').length;
  const html =
    '<div class="mt_primelink-acf-field" data-index="'+i+'">' +
      '<div class="mt_primelink-acf-row">' +
        '<input type="text" class="mt_primelink-acf-label" placeholder="ACF Field Label" />' +
        '<input type="text" class="mt_primelink-acf-key" placeholder="ACF meta key (e.g. field_XXXX or my_field)" />' +
      '</div>' +
      '<div class="mt_primelink-map-slot unmapped" data-target="">' +
        '<div class="mt_primelink-map-drop mt-map-slot__drop"><span class="mt_primelink-muted">Drop field here</span></div>' +
      '</div>' +
      '<input type="hidden" class="mt_primelink-map-input" value="" />' +
      '<input type="hidden" class="mt_primelink-acf-source" value="" />' +
      '<input type="hidden" class="mt_primelink-map-config" value="" />' +
      '<button type="button" class="button-link-delete mt_primelink-acf-remove">Remove</button>' +
    '</div>';
  $('#mt_primelink-acf-fields-wrap').append(html);
  bindDnD();
});

$(document).on('click', '.mt_primelink-acf-remove', function(){
  $(this).closest('.mt_primelink-acf-field').remove();
  pushMapHistory();
  saveStep5Mapping();
});

$(document).on('click', '.mt_primelink-custom-remove', function(){
  $(this).closest('.mt_primelink-custom-field').remove();
  pushMapHistory();
  saveStep5Mapping();
});

// Attribute rows add/remove/toggles
$(document).on('click', '#mt_primelink-add-attribute', function(){
  $('#mt_primelink-attributes-wrap').append(renderAttributeRow('', '', false));
  bindDnD();
  pushMapHistory();
  saveStep5Mapping();
});

$(document).on('click', '.mt_primelink-attr-remove', function(){
  $(this).closest('.mt_primelink-attr-row').remove();
  pushMapHistory();
  saveStep5Mapping();
});

$(document).on('input change', '.mt_primelink-attr-name, .mt_primelink-attr-variation-toggle, #mt_primelink-create-variations', function(){
  // Update label text when attribute name changes
  if($(this).hasClass('mt_primelink-attr-name')){
    const $attrRow = $(this).closest('.mt_primelink-attr-row');
    const name = $(this).val().trim() || 'Attribute Values';
    $attrRow.find('.mt_primelink-map-label__text').text(name);
  }
  saveStep5Mapping();
});

// Product type selector (static vs mapped)
$(document).on('change', '#mt_primelink-product-type-select', function(){
  mt_primelinkSelectedProductType = $(this).val() || 'mapped';
  toggleVariationPanel();
  pushMapHistory();
  saveStep5Mapping();
});

// Product data tabs
$(document).on('click', '.mt_primelink-pd-tab', function(){
  const tab = $(this).data('tab');
  $('.mt_primelink-pd-tab').removeClass('is-active');
  $(this).addClass('is-active');
  $('.mt_primelink-pd-panel').removeClass('is-active');
  $('.mt_primelink-pd-panel[data-panel="'+tab+'"]').addClass('is-active');
});

function toggleVariationPanel(){
  const type = mt_primelinkSelectedProductType;
  const $tab = $('.mt_primelink-pd-tab[data-tab="variations"]');
  const $panel = $('.mt_primelink-pd-panel[data-panel="variations"]');
  const allow = (type === 'variable' || type === 'grouped' || type === 'mapped');
  $tab.toggle(allow);
  $panel.toggle(allow);
  if(!allow && $tab.hasClass('is-active')){
    $('.mt_primelink-pd-tab[data-tab="general"]').trigger('click');
  }
}

// Auto-save when leaving inputs
$(document).on('change blur', '.mt_primelink-map-input, .mt_primelink-custom-key', function(){
  pushMapHistory();
  saveStep5Mapping();
});

// Retry save (step 5)
$(document).on('click', '#mt_primelink-save-retry', function(e){
  e.preventDefault();
  setSaveStatus('saving');
  saveStep5Mapping(true);
});

// Other options autosave
$(document).on('input change', '#mt_primelink-opt-global-prefix, #mt_primelink-opt-global-suffix, #mt_primelink-opt-global-trim, #mt_primelink-opt-slugify, #mt_primelink-opt-fallback-image, #mt_primelink-opt-skip-field', function(){
  saveStep5Mapping();
});


  // Step 5 navigator (vertical preview)
  $(document).on('click', '.mt-map-prev, .mt-map-next', function(e){
    e.preventDefault();
    const idx = parseInt($(this).data('index'), 10) || 0;
    loadMappingPreview(idx);
  });

  // -----------------------------
// Step 5 Advanced Field Editor
// -----------------------------

function getSlotConfig($slot){
  const raw = $slot.find('.mt_primelink-map-config').val();
  if(!raw) return {};
  try { return JSON.parse(raw); } catch(e){ return {}; }
}

function applySeparator(value, separator){
  if(!separator) return value;
  const parts = String(value).split(separator).map(v => v.trim()).filter(Boolean);
  return parts.length > 1 ? parts : value;
}


function setSlotConfig($slot, cfg){
  $slot.find('.mt_primelink-map-config').val(JSON.stringify(cfg || {}));
}

function setSlotBadge($slot, mode){
  $slot.removeClass('is-auto is-manual');
  let $badge = $slot.find('.mt_primelink-map-badge');
  if(!mode){
    $badge.remove();
    return;
  }
  if(!$badge.length){
    $badge = $('<span class="mt_primelink-map-badge"></span>');
    $slot.prepend($badge);
  }
  const label = mode === 'auto' ? 'Auto' : 'Manual';
  $badge.text(label);
  $slot.addClass(mode === 'auto' ? 'is-auto' : 'is-manual');
}

function syncSlotBadges(){
  $('.mt_primelink-map-slot').each(function(){
    const cfg = getSlotConfig($(this));
    const source = $(this).find('.mt_primelink-map-input').val();
    const mode = cfg.source_mode || (source ? 'manual' : '');
    setSlotBadge($(this), mode);
  });
}

function openEditor($slot, selectedSource){
  const target = $slot.data('target');
  let source = $slot.find('.mt_primelink-map-input').val() || '';
  let cfg    = getSlotConfig($slot);

  // If a specific chip was clicked (gallery), set it as primary in the editor
  if(selectedSource){
    const extras = Array.isArray(cfg.extra_sources) ? cfg.extra_sources.slice() : [];
    const all = [source].concat(extras).filter(Boolean);
    if(all.includes(selectedSource)){
      const others = all.filter(f => f !== selectedSource);
      source = selectedSource;
      cfg.extra_sources = others;
    }
  }

  $('#mt_primelink-editor-target').val(target);
  $('#mt_primelink-editor-source').val(source);
  $('#mt_primelink-editor-column-header').val(cfg.column_header || '');
  $('#mt_primelink-editor-prefix').val(cfg.prefix || '');
  $('#mt_primelink-editor-suffix').val(cfg.suffix || '');

  $('#mt_primelink-editor-separator').val(cfg.separator || '');
  $('#mt_primelink-editor-default').val(cfg.default || '');
  $('#mt_primelink-editor-template').val(cfg.template || '');
  const mode = cfg.template_mode || 'before';
  $('input[name="mt_primelink-editor-template-mode"][value="'+mode+'"]').prop('checked', true);

  // Basic transforms
  $('#mt_primelink-editor-trim').prop('checked', cfg.transforms?.trim !== false);
  $('#mt_primelink-editor-lowercase').prop('checked', !!cfg.transforms?.lowercase);
  $('#mt_primelink-editor-uppercase').prop('checked', !!cfg.transforms?.uppercase);
  $('#mt_primelink-editor-capitalize').prop('checked', !!cfg.transforms?.capitalize);
  $('#mt_primelink-editor-title-case').prop('checked', !!cfg.transforms?.title_case);
  $('#mt_primelink-editor-normalize-html').prop('checked', !!cfg.transforms?.normalize_html);
  
  // Advanced transforms
  $('#mt_primelink-editor-remove-accents').prop('checked', !!cfg.transforms?.remove_accents);
  $('#mt_primelink-editor-slugify').prop('checked', !!cfg.transforms?.slugify);
  $('#mt_primelink-editor-extract-numbers').prop('checked', !!cfg.transforms?.extract_numbers);
  $('#mt_primelink-editor-extract-text').prop('checked', !!cfg.transforms?.extract_text);
  $('#mt_primelink-editor-url-encode').prop('checked', !!cfg.transforms?.url_encode);
  $('#mt_primelink-editor-url-decode').prop('checked', !!cfg.transforms?.url_decode);
  
  // Number transforms
  $('#mt_primelink-editor-round').prop('checked', !!cfg.transforms?.round);
  $('#mt_primelink-editor-round-decimals').val(cfg.transforms?.round_decimals ?? 2);
  $('#mt_primelink-editor-ceil').prop('checked', !!cfg.transforms?.ceil);
  $('#mt_primelink-editor-floor').prop('checked', !!cfg.transforms?.floor);
  $('#mt_primelink-editor-number-format').val(cfg.transforms?.number_format || '');
  
  // String operations
  $('#mt_primelink-editor-substring-start').val(cfg.transforms?.substring_start ?? '');
  $('#mt_primelink-editor-substring-length').val(cfg.transforms?.substring_length ?? '');
  $('#mt_primelink-editor-pad-side').val(cfg.transforms?.pad_side || '');
  $('#mt_primelink-editor-pad-length').val(cfg.transforms?.pad_length ?? '');
  $('#mt_primelink-editor-pad-char').val(cfg.transforms?.pad_char || '');
  
  // Date transforms
  $('#mt_primelink-editor-date-format').val(cfg.transforms?.date_format || '');
  $('#mt_primelink-editor-date-output').val(cfg.transforms?.date_output || '');
  
  // Regex
  $('#mt_primelink-editor-regex-pattern').val(cfg.transforms?.regex_pattern || '');
  $('#mt_primelink-editor-regex-replacement').val(cfg.transforms?.regex_replacement || '');

  // Replace rules
  const repWrap = $('#mt_primelink-editor-replace-wrap').empty();
  (cfg.transforms?.replace || []).forEach((r)=>{
    repWrap.append(renderReplaceRow(r.from || '', r.to || ''));
  });

  // Price adjust (only for price targets)
  const isPriceTarget = ['price','regular_price','sale_price'].includes((target||'').toLowerCase());
  if(isPriceTarget){
    $('.mt_primelink-price-adjust-row').show();
    const pa = cfg.price_adjust || {};
    $('#mt_primelink-price-adjust-type').val(pa.type || '');
    $('#mt_primelink-price-adjust-amount').val(pa.amount !== undefined ? pa.amount : '');
  } else {
    $('.mt_primelink-price-adjust-row').hide();
  }

  // Conditions
  const condWrap = $('#mt_primelink-editor-conditions-wrap').empty();
  (cfg.conditions || []).forEach((c)=>{
    condWrap.append(renderConditionRow(c.field || '', c.op || '=', c.value || ''));
  });

  // Gallery extra sources are now handled automatically via drag & drop
  // Removed manual input field

  const $editor = $('#mt_primelink-map-editor');
  $('body').append($editor);
  $editor.addClass('is-open').fadeIn(120);
  $('body').addClass('mt_primelink-editor-open');
}

function closeEditor(){
  $('#mt_primelink-map-editor').removeClass('is-open').fadeOut(120);
  $('body').removeClass('mt_primelink-editor-open');
}

function renderReplaceRow(from, to){
  return `
    <div class="mt_primelink-editor-row mt_primelink-replace-row">
      <input type="text" class="mt_primelink-replace-from" placeholder="from" value="${escapeHtml(from)}" />
      <input type="text" class="mt_primelink-replace-to" placeholder="to" value="${escapeHtml(to)}" />
      <button type="button" class="button-link-delete mt_primelink-replace-remove">Remove</button>
    </div>
  `;
}

function renderConditionRow(field, op, value){
  return `
    <div class="mt_primelink-editor-row mt_primelink-condition-row">
      <input type="text" class="mt_primelink-cond-field" placeholder="field" value="${escapeHtml(field)}" />
      <select class="mt_primelink-cond-op">
        <option value="=" ${op==='='?'selected':''}>=</option>
        <option value="!=" ${op==='!='?'selected':''}>!=</option>
        <option value=">" ${op==='>'?'selected':''}>&gt;</option>
        <option value="<" ${op==='<'?'selected':''}>&lt;</option>
        <option value="contains" ${op==='contains'?'selected':''}>contains</option>
      </select>
      <input type="text" class="mt_primelink-cond-value" placeholder="value" value="${escapeHtml(value)}" />
      <button type="button" class="button-link-delete mt_primelink-cond-remove">Remove</button>
    </div>
  `;
}

// Enable edit after drop
function enableEditButton($slot){
  $slot.find('.mt_primelink-map-edit').prop('disabled', false);
}

// Hook into your existing drop handler:
$(document).on('drop', '.mt-map-slot__drop', function(){
  enableEditButton($(this).closest('.mt_primelink-map-slot'));
});

// Edit click
$(document).on('click', '.mt_primelink-map-edit', function(){
  const $slot = $(this).closest('.mt_primelink-map-slot');
  openEditor($slot);
});

// Handle Edit button for attributes
$(document).on('click', '.mt_primelink-attr-edit', function(){
  const $attr = $(this).closest('.mt_primelink-attr-row');
  if($attr.length){
    openAttributeEditor($attr);
  }
});

$(document).on('click', '.mt_primelink-map-editor-close', closeEditor);

// Live preview: Update preview when mapping editor changes (debounced)
$(document).on('input change', '#mt_primelink-map-editor input, #mt_primelink-map-editor textarea, #mt_primelink-map-editor select, #mt_primelink-map-editor input[type="checkbox"], #mt_primelink-map-editor input[type="radio"]', function(){
  // Only trigger if we're on step 5 and editor is open
  if($('.mt_primelink-import-step-5').length && $('#mt_primelink-map-editor').is(':visible')){
    triggerLivePreview();
  }
});

// Add replace / condition rows
$(document).on('click', '#mt_primelink-editor-add-replace', function(){
  $('#mt_primelink-editor-replace-wrap').append(renderReplaceRow('', ''));
});
$(document).on('click', '.mt_primelink-replace-remove', function(){
  $(this).closest('.mt_primelink-replace-row').remove();
});

$(document).on('click', '#mt_primelink-editor-add-condition', function(){
  $('#mt_primelink-editor-conditions-wrap').append(renderConditionRow('', '=', ''));
});
$(document).on('click', '.mt_primelink-cond-remove', function(){
  $(this).closest('.mt_primelink-condition-row').remove();
});

// Save editor state into slot + autosave step5
$(document).on('click', '#mt_primelink-editor-save', function(){
  const target = $('#mt_primelink-editor-target').val();
  const $slot  = $('.mt_primelink-map-slot[data-target="'+target+'"]');
  const $attr  = $('#mt_primelink-map-editor').data('attr-row');

  // Handle attribute rows
  if($attr && $attr.length){
    const newSource = $('#mt_primelink-editor-source').val().trim();
    if(newSource){
      $attr.find('.mt_primelink-attr-source').val(newSource);
      // Update the drop zone display
      const $drop = $attr.find('.mt-map-slot__drop');
      $drop.html(buildChip(newSource));
      // Enable Edit button
      $attr.find('.mt_primelink-attr-edit').prop('disabled', false).addClass('is-visible');
    }

    const cfg = {
      column_header: ($('#mt_primelink-editor-column-header').val() || '').trim(),
      separator: ($('#mt_primelink-editor-separator').val() || '').trim(),
      default: ($('#mt_primelink-editor-default').val() || '').trim(),
      prefix: ($('#mt_primelink-editor-prefix').val() || '').trim(),
      suffix: ($('#mt_primelink-editor-suffix').val() || '').trim(),
      template: $('#mt_primelink-editor-template').val(),
      template_mode: $('input[name="mt_primelink-editor-template-mode"]:checked').val() || 'before',
      transforms: {
        trim: $('#mt_primelink-editor-trim').is(':checked'),
        lowercase: $('#mt_primelink-editor-lowercase').is(':checked'),
        uppercase: $('#mt_primelink-editor-uppercase').is(':checked'),
        capitalize: $('#mt_primelink-editor-capitalize').is(':checked'),
        title_case: $('#mt_primelink-editor-title-case').is(':checked'),
        normalize_html: $('#mt_primelink-editor-normalize-html').is(':checked'),
        remove_accents: $('#mt_primelink-editor-remove-accents').is(':checked'),
        slugify: $('#mt_primelink-editor-slugify').is(':checked'),
        extract_numbers: $('#mt_primelink-editor-extract-numbers').is(':checked'),
        extract_text: $('#mt_primelink-editor-extract-text').is(':checked'),
        url_encode: $('#mt_primelink-editor-url-encode').is(':checked'),
        url_decode: $('#mt_primelink-editor-url-decode').is(':checked'),
        round: $('#mt_primelink-editor-round').is(':checked'),
        round_decimals: parseInt($('#mt_primelink-editor-round-decimals').val()) || 2,
        ceil: $('#mt_primelink-editor-ceil').is(':checked'),
        floor: $('#mt_primelink-editor-floor').is(':checked'),
        number_format: $('#mt_primelink-editor-number-format').val() || '',
        substring_start: $('#mt_primelink-editor-substring-start').val() || '',
        substring_length: $('#mt_primelink-editor-substring-length').val() || '',
        pad_side: $('#mt_primelink-editor-pad-side').val() || '',
        pad_length: parseInt($('#mt_primelink-editor-pad-length').val()) || 0,
        pad_char: $('#mt_primelink-editor-pad-char').val() || '',
        date_format: $('#mt_primelink-editor-date-format').val() || '',
        date_output: $('#mt_primelink-editor-date-output').val() || '',
        regex_pattern: $('#mt_primelink-editor-regex-pattern').val() || '',
        regex_replacement: $('#mt_primelink-editor-regex-replacement').val() || '',
        replace: []
      },
      conditions: []
    };

    // Collect replace rules
    $('#mt_primelink-editor-replace-wrap .mt_primelink-replace-row').each(function(){
      const from = $(this).find('.mt_primelink-replace-from').val().trim();
      const to = $(this).find('.mt_primelink-replace-to').val().trim();
      if(from || to){
        cfg.transforms.replace.push({from, to});
      }
    });

    // Collect conditions
    $('#mt_primelink-editor-conditions-wrap .mt_primelink-condition-row').each(function(){
      const field = $(this).find('.mt_primelink-cond-field').val().trim();
      const op = $(this).find('.mt_primelink-cond-op').val();
      const value = $(this).find('.mt_primelink-cond-value').val().trim();
      if(field){
        cfg.conditions.push({field, op, value});
      }
    });

    $attr.find('.mt_primelink-attr-config').val(JSON.stringify(cfg));
    pushMapHistory();
    saveStep5Mapping();
    closeEditor();
    return;
  }

  if(!$slot.length){
    closeEditor();
    return;
  }

  const newSource = $('#mt_primelink-editor-source').val().trim();
  if(newSource){
    $slot.find('.mt_primelink-map-input').val(newSource);
  }

  const cfg = {
    column_header: ($('#mt_primelink-editor-column-header').val() || '').trim(),
    separator: ($('#mt_primelink-editor-separator').val() || '').trim(),
    default: ($('#mt_primelink-editor-default').val() || '').trim(),
    prefix: ($('#mt_primelink-editor-prefix').val() || '').trim(),
    suffix: ($('#mt_primelink-editor-suffix').val() || '').trim(),
    template: $('#mt_primelink-editor-template').val(),
    template_mode: $('input[name="mt_primelink-editor-template-mode"]:checked').val() || 'before',
    transforms: {
      trim: $('#mt_primelink-editor-trim').is(':checked'),
      lowercase: $('#mt_primelink-editor-lowercase').is(':checked'),
      uppercase: $('#mt_primelink-editor-uppercase').is(':checked'),
      capitalize: $('#mt_primelink-editor-capitalize').is(':checked'),
      title_case: $('#mt_primelink-editor-title-case').is(':checked'),
      normalize_html: $('#mt_primelink-editor-normalize-html').is(':checked'),
      remove_accents: $('#mt_primelink-editor-remove-accents').is(':checked'),
      slugify: $('#mt_primelink-editor-slugify').is(':checked'),
      extract_numbers: $('#mt_primelink-editor-extract-numbers').is(':checked'),
      extract_text: $('#mt_primelink-editor-extract-text').is(':checked'),
      url_encode: $('#mt_primelink-editor-url-encode').is(':checked'),
      url_decode: $('#mt_primelink-editor-url-decode').is(':checked'),
      round: $('#mt_primelink-editor-round').is(':checked'),
      round_decimals: parseInt($('#mt_primelink-editor-round-decimals').val()) || 2,
      ceil: $('#mt_primelink-editor-ceil').is(':checked'),
      floor: $('#mt_primelink-editor-floor').is(':checked'),
      number_format: $('#mt_primelink-editor-number-format').val() || '',
      substring_start: $('#mt_primelink-editor-substring-start').val() || '',
      substring_length: $('#mt_primelink-editor-substring-length').val() || '',
      pad_side: $('#mt_primelink-editor-pad-side').val() || '',
      pad_length: parseInt($('#mt_primelink-editor-pad-length').val()) || 0,
      pad_char: $('#mt_primelink-editor-pad-char').val() || '',
      date_format: $('#mt_primelink-editor-date-format').val() || '',
      date_output: $('#mt_primelink-editor-date-output').val() || '',
      regex_pattern: $('#mt_primelink-editor-regex-pattern').val() || '',
      regex_replacement: $('#mt_primelink-editor-regex-replacement').val() || '',
      replace: []
    },
    conditions: []
  };

  const paType = $('#mt_primelink-price-adjust-type').val() || '';
  const paAmount = parseFloat($('#mt_primelink-price-adjust-amount').val());
  if(paType && !isNaN(paAmount)){
    cfg.price_adjust = { type: paType, amount: paAmount };
  }

  // Collect replace rules
  $('#mt_primelink-editor-replace-wrap .mt_primelink-replace-row').each(function(){
    const from = $(this).find('.mt_primelink-replace-from').val();
    const to   = $(this).find('.mt_primelink-replace-to').val();
    if(from !== '') cfg.transforms.replace.push({from, to});
  });

  // Collect conditions
  $('#mt_primelink-editor-conditions-wrap .mt_primelink-condition-row').each(function(){
    const field = $(this).find('.mt_primelink-cond-field').val().trim();
    const op    = $(this).find('.mt_primelink-cond-op').val();
    const value = $(this).find('.mt_primelink-cond-value').val().trim();
    if(field !== '') cfg.conditions.push({field, op, value});
  });

  // Gallery extra sources are now handled automatically via drag & drop
  // No need to read from manual input field - extra_sources are set when dragging multiple fields to gallery slot
  const existingCfg = getSlotConfig($slot);
  cfg.source_mode = existingCfg.source_mode || 'manual';

  setSlotConfig($slot, cfg);
  setSlotBadge($slot, cfg.source_mode || "manual");
  
  // Update custom column name display if this is a custom column
  if(target && target.startsWith('custom_column_')){
    const columnName = cfg.column_header || target;
    const $labelText = $slot.find('.mt_primelink-map-label__text[data-editable="true"]');
    if($labelText.length){
      $labelText.text(columnName);
      $slot.find('.mt_primelink-custom-column-name').val(columnName);
    }
  }
  
  // Save mapping (works for both import and export)
  if(typeof saveStep5Mapping === 'function'){
    saveStep5Mapping();
  }
  if(typeof saveExportStep2Mapping === 'function'){
    saveExportStep2Mapping();
  }
  
  if(typeof loadMappingPreview === 'function'){
    // Use live preview with loading indicator
    triggerLivePreview(true); // Immediate update after save
  }
  if(typeof loadExportPreview === 'function'){
    loadExportPreview();
  }
  closeEditor();
});

// -----------------------------
// Step 5 Templates (Save/Load/List/Delete)
// -----------------------------

/**
 * Get import type from draft, URL, or body class (in that order of priority)
 * @returns {Promise<string>} Import type: 'product', 'order', 'customer', or 'coupon'
 */
async function getImportType(){
  const importId = currentImportId();
  
  // First, try to get from draft (most reliable)
  if(importId && importId > 0){
    try {
      const draft = await restPost('/import/get-draft', { import_id: importId });
      if(draft && draft.draft){
        let type = draft.draft.type || (draft.draft.step_data && draft.draft.step_data[1] && draft.draft.step_data[1].type);
        if(type){
          // Normalize plural to singular
          if(type === 'orders') type = 'order';
          else if(type === 'customers') type = 'customer';
          else if(type === 'coupons') type = 'coupon';
          else if(type === 'products') type = 'product';
          else if(type === 'categories') type = 'category';
          else if(type === 'tags') type = 'tag';
          if(['product', 'order', 'customer', 'coupon', 'category', 'tag'].includes(type)){
            return type;
          }
        }
      }
    } catch(e){
      // Draft fetch failed (404 or other error), continue to fallbacks silently
      // Don't log or show error - this is expected when draft doesn't exist yet
    }
  }
  
  // Fallback to URL parameter
  const url = new URL(window.location.href);
  let importType = url.searchParams.get('import_type') || '';
  
  // Normalize plural to singular (orders -> order, customers -> customer, coupons -> coupon, products -> product, categories -> category)
  if(importType === 'orders'){
    importType = 'order';
  } else if(importType === 'customers'){
    importType = 'customer';
  } else if(importType === 'coupons'){
    importType = 'coupon';
  } else if(importType === 'products'){
    importType = 'product';
  } else if(importType === 'categories'){
    importType = 'category';
  } else if(importType === 'tags'){
    importType = 'tag';
  } else if(importType === 'attributes'){
    importType = 'attribute';
        } else if(importType === 'variations'){
          importType = 'variation';
        } else if(importType === 'reviews'){
          importType = 'review';
        }

        if(['product', 'order', 'customer', 'coupon', 'category', 'tag', 'attribute', 'variation', 'review'].includes(importType)){
    return importType;
  }
  
  // Fallback to body class
  if(document.body.classList.contains('mt_primelink-import-orders')){
    return 'order';
  } else if(document.body.classList.contains('mt_primelink-import-customers')){
    return 'customer';
  } else if(document.body.classList.contains('mt_primelink-import-coupons')){
    return 'coupon';
  } else if(document.body.classList.contains('mt_primelink-import-categories')){
    return 'category';
  } else if(document.body.classList.contains('mt_primelink-import-tags')){
    return 'tag';
  }
  
  // Default to product
  return 'product';
}

async function getExportType(){
  // First, try URL parameter (most immediate)
  const url = new URL(window.location.href);
  let exportType = url.searchParams.get('export_type') || '';
  
  // Normalize plural to singular (orders -> order, customers -> customer, coupons -> coupon, products -> product, categories -> category, tags -> tag, etc.)
  if(exportType === 'orders'){
    exportType = 'order';
  } else if(exportType === 'customers'){
    exportType = 'customer';
  } else if(exportType === 'coupons'){
    exportType = 'coupon';
  } else if(exportType === 'products'){
    exportType = 'product';
  } else if(exportType === 'categories'){
    exportType = 'category';
  } else if(exportType === 'tags'){
    exportType = 'tag';
  } else if(exportType === 'attributes'){
    exportType = 'attribute';
  } else if(exportType === 'variations'){
    exportType = 'variation';
  } else if(exportType === 'reviews'){
    exportType = 'review';
  } else if(exportType === 'downloads'){
    exportType = 'download';
  }
  
  if(['product', 'order', 'customer', 'coupon', 'category', 'tag', 'attribute', 'variation', 'review', 'download'].includes(exportType)){
    return exportType;
  }
  
  // Second, try to get from draft
  const exportId = currentExportId();
  if(exportId){
    try {
      const draft = await restPost('/export/get-draft', { export_id: exportId });
      if(draft && draft.draft){
        let type = draft.draft.type || (draft.draft.step_data && draft.draft.step_data[1] && draft.draft.step_data[1].export_type);
        if(type){
          // Normalize plural to singular
          if(type === 'orders') type = 'order';
          else if(type === 'customers') type = 'customer';
          else if(type === 'coupons') type = 'coupon';
          else if(type === 'products') type = 'product';
          else if(type === 'categories') type = 'category';
          else if(type === 'tags') type = 'tag';
          else if(type === 'attributes') type = 'attribute';
          else if(type === 'variations') type = 'variation';
          else if(type === 'reviews') type = 'review';
          else if(type === 'downloads') type = 'download';

          if(['product', 'order', 'customer', 'coupon', 'category', 'tag', 'attribute', 'variation', 'review', 'download'].includes(type)){
            return type;
          }
        }
      }
    } catch(e){
      // Draft fetch failed, continue to fallbacks
    }
  }
  
  // Fallback to hidden input field (from step-1.php templates)
  const $typeInput = $('#mt-primelink-export-type');
  if($typeInput.length){
    let type = ($typeInput.val() || '').trim();
    if(type){
      // Normalize plural to singular
      if(type === 'orders') type = 'order';
      else if(type === 'customers') type = 'customer';
      else if(type === 'coupons') type = 'coupon';
      else if(type === 'products') type = 'product';
      else if(type === 'categories') type = 'category';
      else if(type === 'tags') type = 'tag';
      else if(type === 'attributes') type = 'attribute';
      else if(type === 'variations') type = 'variation';
      else if(type === 'reviews') type = 'review';
      else if(type === 'downloads') type = 'download';

      if(['product', 'order', 'customer', 'coupon', 'category', 'tag', 'attribute', 'variation', 'review', 'download'].includes(type)){
        return type;
      }
    }
  }
  
  // Fallback to body class
  if(document.body.classList.contains('mt_primelink-export-orders')){
    return 'order';
  } else if(document.body.classList.contains('mt_primelink-export-customers')){
    return 'customer';
  } else if(document.body.classList.contains('mt_primelink-export-coupons')){
    return 'coupon';
  } else if(document.body.classList.contains('mt_primelink-export-categories')){
    return 'category';
  } else if(document.body.classList.contains('mt_primelink-export-tags')){
    return 'tag';
  } else if(document.body.classList.contains('mt_primelink-export-attributes')){
    return 'attribute';
  } else if(document.body.classList.contains('mt_primelink-export-variations')){
    return 'variation';
  } else if(document.body.classList.contains('mt_primelink-export-reviews')){
    return 'review';
  } else if(document.body.classList.contains('mt_primelink-export-downloads')){
    return 'download';
  }
  
  // Fallback to data attribute from wizard
  const $dataAttr = $('[data-export-type]');
  if($dataAttr.length){
    let type = $dataAttr.data('export-type') || $dataAttr.attr('data-export-type') || '';
    if(type){
      // Normalize plural to singular
      if(type === 'orders') type = 'order';
      else if(type === 'customers') type = 'customer';
      else if(type === 'coupons') type = 'coupon';
      else if(type === 'products') type = 'product';
      else if(type === 'categories') type = 'category';
      else if(type === 'tags') type = 'tag';
      if(['product', 'order', 'customer', 'coupon', 'category', 'tag'].includes(type)){
        return type;
      }
    }
  }
  
  // Default to product
  return 'product';
}

// Prevent multiple simultaneous calls to refreshTemplateList
let refreshTemplateListInProgress = false;

async function refreshTemplateList(selectNameToKeep){
  const $select = $('#mt_primelink-load-template');
  if(!$select.length) {
    return;
  }

  // Prevent concurrent calls
  if(refreshTemplateListInProgress){
    return;
  }

  refreshTemplateListInProgress = true;

  try {
    // Get import type to filter templates
    const importType = await getImportType();
    
    // If importType is undefined or empty, don't make the request (would return all templates)
    if(!importType || importType === ''){
      refreshTemplateListInProgress = false;
      return;
    }

    // Request both templates and imports filtered by type
    const [templatesRes, importsRes] = await Promise.all([
      restGet('/import/templates?type=' + encodeURIComponent(importType) + '&ts=' + Date.now()).catch(() => ({ templates: [] })),
      restGet('/import/list?type=' + encodeURIComponent(importType) + '&ts=' + Date.now()).catch(() => ({ imports: [] }))
    ]);
    
    const templateNames = templatesRes.templates || [];
    const imports = importsRes.imports || [];

    let html = '<option value="">Load existing template or import...</option>';
    
    // Add templates section
    if(templateNames.length > 0){
      html += '<optgroup label="Templates">';
      templateNames.forEach(n=>{
        // Remove prefix for display (support all types)
        const displayName = n.replace(/^(order|product|customer|coupon|category|tag|attribute|variation|review|download):/, '');
        html += '<option value="template:'+escapeHtml(n)+'">📋 '+escapeHtml(displayName)+'</option>';
      });
      html += '</optgroup>';
    }
    
    // Add imports section
    if(imports.length > 0){
      html += '<optgroup label="Imports">';
      imports.forEach(imp=>{
        html += '<option value="import:'+escapeHtml(imp.id)+'">🔄 '+escapeHtml(imp.display_name)+'</option>';
      });
      html += '</optgroup>';
    }

    $select.html(html);

    if(selectNameToKeep){
      $select.val(selectNameToKeep);
    }
  } catch(e){
    // If list fails, keep current options
  } finally {
    refreshTemplateListInProgress = false;
  }
}

function applyTemplateToUI(data){
  if(!data || typeof data !== 'object') {
    return;
  }

  const mapping = data.mapping || {};
  const other = data.other_options || {};

  // CRITICAL: Open all accordions and tabs BEFORE applying mapping
  // This ensures all slots are in DOM and visible when we apply the template
  $('details.mt_primelink-accordion').attr('open', true);
  
  // Open all product data tabs
  $('.mt_primelink-pd-tab').each(function(){
    const tabName = $(this).data('tab');
    if(tabName){
      $(this).addClass('is-active').trigger('click');
      $('.mt_primelink-pd-panel[data-panel="' + tabName + '"]').addClass('is-active');
    }
  });
  
  // Force all panels to be visible
  $('.mt_primelink-pd-panel').addClass('is-active');

  // Apply normal slots - use a more robust selector that finds ALL slots, even in closed accordions
  const allSlots = $('.mt_primelink-map-slot');
  
  allSlots.each(function(){
    const $slot = $(this);
    const target = $slot.data('target');
    
    if(!target) {
      return; // Skip slots without target (like ACF fields)
    }
    
    const cfg = mapping[target] || null;

    const $drop  = $slot.find('.mt-map-slot__drop');
    const $input = $slot.find('.mt_primelink-map-input');
    const $conf  = $slot.find('.mt_primelink-map-config');
    const $edit  = $slot.find('.mt_primelink-map-edit');

    if(cfg && cfg.source){
      const chips = [cfg.source].concat(Array.isArray(cfg.extra_sources) ? cfg.extra_sources : []).filter(Boolean).map(buildChip).join(' ');
      $drop.html(chips || buildChip(cfg.source));
      $input.val(cfg.source);
      $conf.val(JSON.stringify(cfg));
      $edit.prop('disabled', false).addClass('is-visible');
      $slot.removeClass('unmapped');
      setSlotBadge($slot, cfg.source_mode || 'manual');
    } else {
      // Only clear if we have mapping data but this target is not in it
      // Don't clear if mapping is empty (might be loading)
      if(Object.keys(mapping).length > 0){
        $drop.html('<span class="mt_primelink-muted">Drop field here</span>');
        $input.val('');
        $conf.val('');
        $edit.prop('disabled', true).removeClass('is-visible');
        setSlotBadge($slot, '');
      }
    }
  });

  // Attributes (rebuild from mapping keys)
  const attrWrap = $('#mt_primelink-attributes-wrap').empty();
  let attrCount = 0;
  Object.keys(mapping || {}).forEach(key => {
    if(key.indexOf('attribute:') === 0){
      const name = key.substring(10);
      const cfg  = mapping[key] || {};
      attrWrap.append(renderAttributeRow(name, cfg.source || '', !!cfg.variation));
      attrCount++;
    }
  });
  if(!attrCount && Array.isArray(data.attributes)){
    data.attributes.forEach(a=>{
      attrWrap.append(renderAttributeRow(a.name || '', a.source || '', !!a.variation));
    });
  }
  $('#mt_primelink-create-variations').prop('checked', !!data.create_variations);
  // Product type select
  const pt = data.product_type || (mapping.product_type && mapping.product_type.constant ? mapping.product_type.constant : 'simple');
  mt_primelinkSelectedProductType = pt || 'simple';
  $('#mt_primelink-product-type-select').val(mt_primelinkSelectedProductType);

  // Gallery slots (rebuild additional slots)
  const gallerySlotsWrap = $('#mt_primelink-gallery-slots');
  if(gallerySlotsWrap.length){
    // Find all images_* slots in mapping
    Object.keys(mapping || {}).forEach(target => {
      if(target.startsWith('images_') && target !== 'images'){
        const match = target.match(/^images_(\d+)$/);
        if(match){
          const index = parseInt(match[1], 10);
          const cfg = mapping[target] || {};
          const $existing = gallerySlotsWrap.find('.mt_primelink-map-slot[data-target="' + target + '"]');
          if(!$existing.length){
            const $slot = renderGallerySlot(target, index, cfg.source || '', cfg ? JSON.stringify(cfg) : '');
            gallerySlotsWrap.append($slot);
            bindDnD();
          }
        }
      }
    });
    initGallerySlotsCounter();
  }

  // Custom fields (rebuild)
  const customWrap = $('#mt_primelink-custom-fields-wrap').empty();
  (data.custom_fields || []).forEach(cf=>{
    const hasSource = cf.source && cf.source.trim();
    const unmappedClass = hasSource ? '' : ' unmapped';
    const dropHtml = hasSource
      ? buildChip(cf.source)
      : '<span class="mt_primelink-muted">Drop field here</span>';
    const html =
      '<div class="mt_primelink-custom-field">' +
        '<input type="text" class="mt_primelink-custom-key" placeholder="Meta key" value="'+escapeHtml(cf.key || '')+'" />' +
        '<div class="mt_primelink-map-slot'+unmappedClass+'">' +
          '<div class="mt_primelink-map-drop mt-map-slot__drop">' + dropHtml + '</div>' +
        '</div>' +
        '<input type="hidden" class="mt_primelink-map-input" value="'+escapeHtml(cf.source || '')+'" />' +
        '<input type="hidden" class="mt_primelink-custom-val" value="'+escapeHtml(cf.source || '')+'" />' +
        '<input type="hidden" class="mt_primelink-map-config" value="'+escapeHtml(JSON.stringify(cf || {}))+'" />' +
        '<button type="button" class="button-link-delete mt_primelink-custom-remove">Remove</button>' +
      '</div>';
    customWrap.append(html);
  });

// ACF fields (rebuild)
const acfWrap = $('#mt_primelink-acf-fields-wrap').empty();
(data.acf_fields || []).forEach((acf, index)=>{
  const hasSource = acf.source && acf.source.trim();
  const unmappedClass = hasSource ? '' : ' unmapped';
  const dropHtml = hasSource
    ? buildChip(acf.source)
    : '<span class="mt_primelink-muted">Drop field here</span>';
  // Use acf directly for config, not acf.config (since collectMappingState saves the whole object)
  const acfConfig = acf.config || acf;
  const html =
    '<div class="mt_primelink-acf-field" data-index="'+index+'">' +
      '<div class="mt_primelink-acf-row">' +
        '<input type="text" class="mt_primelink-acf-label" placeholder="ACF Field Label" value="'+escapeHtml(acf.label || '')+'" />' +
        '<input type="text" class="mt_primelink-acf-key" placeholder="ACF meta key (e.g. field_XXXX or my_field)" value="'+escapeHtml(acf.key || '')+'" />' +
      '</div>' +
      '<div class="mt_primelink-map-slot'+unmappedClass+'" data-target="">' +
        '<div class="mt_primelink-map-drop mt-map-slot__drop">' + dropHtml + '</div>' +
      '</div>' +
      '<input type="hidden" class="mt_primelink-map-input" value="'+escapeHtml(acf.source || '')+'" />' +
      '<input type="hidden" class="mt_primelink-acf-source" value="'+escapeHtml(acf.source || '')+'" />' +
      '<input type="hidden" class="mt_primelink-map-config" value="'+escapeHtml(JSON.stringify(acfConfig || {}))+'" />' +
      '<button type="button" class="button-link-delete mt_primelink-acf-remove">Remove</button>' +
    '</div>';
  acfWrap.append(html);
});

if((data.acf_fields || []).length || (data.custom_fields || []).length){
  bindDnD();
}

  bindDnD();
  
  // Row filters (rebuild)
  const filters = data.row_filters || [];
  if(Array.isArray(filters)){
    $('#mt_primelink-filter-rows').empty();
    mt_primelinkFilterIdx = 0;
    if(filters.length > 0){
      filters.forEach(f => {
        if(f && (f.field || f.value)){
          addFilterRow(f.field || '', f.op || '=', f.value || '');
        }
      });
    } else {
      addFilterRow();
    }
    updateFilterUI();
  }
  
  // Mark unmapped slots AFTER applying template
  markUnmappedSlots();
  
  // CRITICAL: Save mapping to draft FIRST, then load preview
  // This ensures the backend has the mapping before we request preview
  const importId = currentImportId();
  
  if(importId && importId > 0){
    // Save mapping to draft first (with a small delay to ensure DOM is ready)
    setTimeout(function(){
      saveStep5Mapping(true); // immediate=true to save right away
      
      // Then load preview after mapping is saved
      setTimeout(function(){
        loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
      }, 500); // Wait 500ms for save to complete
    }, 500); // Wait 500ms for DOM to settle
  }

  // Aliases
  const aliasWrap = $('#mt_primelink-alias-wrap').empty();
  const aliases = data.custom_aliases || [];
  if(aliases.length){
    aliases.forEach(a=>{
      aliasWrap.append(renderAliasRow(a.target || '', (a.aliases || []).join(', ')));
    });
  } else {
    aliasWrap.html('<div class="mt_primelink-empty">No custom aliases yet.</div>');
  }

  // Other options
  $('#mt_primelink-opt-global-prefix').val(other.global_prefix || '');
  $('#mt_primelink-opt-global-suffix').val(other.global_suffix || '');
  $('#mt_primelink-opt-global-trim').prop('checked', !!other.global_trim);
  $('#mt_primelink-opt-slugify').prop('checked', !!other.slugify);
  $('#mt_primelink-opt-fallback-image').val(other.fallback_image || '');
  $('#mt_primelink-opt-skip-field').val(other.skip_if_empty || '');
}

// Save template (IMPORT side) – support both old/new IDs (underscore vs hyphen)
$(document).on('click', '#mt_primelink-save-template, #mt-primelink-save-template', async function(e){
  // Determine context: import vs export
  const isExportPage = $('.mt_primelink-export-step-2').length > 0 || $('body').hasClass('mt_primelink-export-wizard');
  const isImportPage = $('.mt_primelink-import-step-5').length > 0 || $('body').hasClass('mt_primelink-import-wizard') || $('.mt_primelink-import-step-5').length > 0;

  // If we are clearly on export and not on import, let the export handler manage this button
  if (isExportPage && !isImportPage) {
    return;
  }

  const name = ($('#mt_primelink-template-name').val() || '').trim();
  if(!name){
    showToastOrAlert('warning', 'Please enter a template name.', 'Template');
    return;
  }

  // Get import type using helper function
  const importType = await getImportType();

  const payload = collectMappingState();

  try {
    const saved = await restPost('/import/templates/save', {
      name: name,
      data: payload,
      type: importType
    });

    // Clear input
    $('#mt_primelink-template-name').val('');

    // Then do a real refresh from server (will include the prefixed name)
    // Add small delay to ensure server has saved the template
    await new Promise(resolve => setTimeout(resolve, 100));
    await refreshTemplateList(saved.name || saved.saved?.name);

    showToastOrAlert('success', 'Template saved.', 'Template');
  } catch(e){
    showToastOrAlert('error', 'Template save failed.', 'Template');
  }
});

// Load template via button (IMPORT): load directly via REST API, no page refresh
// Support both old/new IDs (underscore vs hyphen) just in case.
// Use higher priority to ensure this runs BEFORE export handler
$(document).on('click', '#mt_primelink-load-template-btn, #mt-primelink-load-template-btn', async function(e){
  // Check if we're on import page FIRST - if so, handle it here
  const isImportPage = $('.mt_primelink-import-step-5').length > 0 || $('body').hasClass('mt_primelink-import-wizard');
  const isExportPage = $('.mt_primelink-export-step-2').length > 0 || $('body').hasClass('mt_primelink-export-wizard');
  
  // If we're clearly on export page and NOT on import page, let export handler manage it
  if(isExportPage && !isImportPage){
    return; // Let export handler manage it
  }
  
  // If we're not on import page, don't handle it here (let export handler take over)
  if(!isImportPage){
    return; // Not on import page, let export handler manage it
  }

  // We're on import page - handle it here
  // Stop all event propagation to prevent export handler from running
  e.preventDefault();
  e.stopPropagation();
  e.stopImmediatePropagation();
  
  const selected = $('#mt_primelink-load-template').val();
  
  if(!selected){
    showToastOrAlert('warning', 'Select a template or import first.', 'Load');
    return;
  }
  
  // Ensure mtPrimeLink is available
  if (typeof mtPrimeLink === 'undefined' || !mtPrimeLink.restUrl) {
    showToastOrAlert('error', 'Configuration error: REST API URL not available. Please refresh the page.', 'Load');
    return false;
  }
  
  try {
    let templateData;
    
    // Check if it's an import (format: "import:123") or template (format: "template:name" or just "name")
    if(selected.startsWith('import:')){
      const importId = selected.replace('import:', '');
      const res = await restPost('/import/templates/load', { import_id: parseInt(importId, 10) });
      
      // Check if response indicates import not found
      if(res && res.message && res.message.includes('not found')){
        showToastOrAlert('error', 'Load failed: ' + res.message, 'Load');
        return false;
      }
      
      templateData = res.data || res || {};
      showToastOrAlert('success', 'Import mapping loaded.', 'Load');
    } else {
      // It's a template
      const templateName = selected.startsWith('template:') ? selected.replace('template:', '') : selected;
      const res = await restPost('/import/templates/load', { name: templateName });
      
      // Check if response indicates template not found (could be 200 with error message)
      if(res && res.message && (res.message.includes('not found') || res.message.includes('Template not found'))){
        showToastOrAlert('error', 'Load failed: ' + res.message, 'Load');
        return false;
      }
      
      // Response structure: { name, data, is_import } where 'data' is the mapping
      // Check if we have valid data
      if (!res || (!res.data && !res.mapping && Object.keys(res).length === 0)) {
        showToastOrAlert('error', 'Load failed: Invalid response from server.', 'Load');
        return false;
      }
      
      templateData = res.data || res || {};
      
      // Don't show toast here - will be handled by auto-load or user action
    }
    
    // Apply template/import data to UI
    if (templateData && (templateData.mapping || Object.keys(templateData).length > 0)) {
      applyTemplateToUI(templateData);
      // Only show success if we're not auto-loading from URL
      const url = new URL(window.location.href);
      if (!url.searchParams.get('template')) {
        showToastOrAlert('success', 'Template loaded.', 'Load');
      }
    } else {
      showToastOrAlert('warning', 'Template data is empty or invalid.', 'Load');
    }
    
  } catch(e){
    // Handle 404 responses that contain error messages
    if(e.status === 404 && e.json && e.json.message){
      showToastOrAlert('error', 'Load failed: ' + e.json.message, 'Load');
      return false;
    }
    // Handle other errors
    const errorMsg = (e.json && e.json.message) || e.message || 'Unknown error';
    showToastOrAlert('error', 'Load failed: ' + errorMsg, 'Load');
    return false;
  }
  
  return false; // Prevent any other handlers
});

// (Optional) If you prefer auto-load on change, we can later hook change event
// to also redirect. For now we keep it manual via the Load button to avoid
// surprising the user.

// Duplicate template (save current state under a new name)
$(document).on('click', '#mt_primelink-duplicate-template', async function(){
  const current = $('#mt_primelink-load-template').val() || '';
  // Remove prefix for prompt (support all types)
  const currentDisplay = current.replace(/^(order|product|customer|coupon|category|tag|attribute|variation|review|download):/, '');
  const newName = prompt('New template name:', currentDisplay ? currentDisplay + ' copy' : '');
  if(!newName) return;

  // Get import type using helper function
  const importType = await getImportType();

  const payload = collectMappingState();
  try {
    const saved = await restPost('/import/templates/save', {
      name: newName,
      data: payload,
      type: importType
    });
    await refreshTemplateList(saved.name);
    if (window.mtPrimeLinkToast) {
      window.mtPrimeLinkToast.success('Template duplicated.', 'Template');
    } else {
    alert('Template duplicated.');
    }
  } catch(e){
    showToastOrAlert('error', 'Could not duplicate template.', 'Template');
  }
});

// Alias add/remove
$(document).on('click', '#mt_primelink-add-alias', function(){
  const $wrap = $('#mt_primelink-alias-wrap');
  if($wrap.find('.mt_primelink-alias-row').length === 0){
    $wrap.empty();
  }
  $wrap.append(renderAliasRow('', ''));
});

$(document).on('click', '.mt_primelink-alias-remove', function(){
  $(this).closest('.mt_primelink-alias-row').remove();
  if($('#mt_primelink-alias-wrap .mt_primelink-alias-row').length === 0){
    $('#mt_primelink-alias-wrap').html('<div class="mt_primelink-empty">No custom aliases yet.</div>');
  }
});

// Refresh list when entering step 5 (import)
$(document).on('mt_primelink_step_changed', function(e, step){
  if(parseInt(step,10) === 5){
    refreshTemplateList();
  }
  // Refresh export templates when entering step 2 (export)
  if(parseInt(step,10) === 2 && $('.mt_primelink-export-step-2').length){
    refreshExportTemplateList();
  }
  // Load step 6 when entering step 6 (import)
  if(parseInt(step,10) === 6 && $('.mt_primelink-import-step-6').length){
    loadStep6();
  }
});

// Load templates immediately when step 5 renders on page load
$(function(){
  const $select = $('#mt_primelink-load-template');
  if($select.length){
    // Check if we're on export or import page
    if($('body').hasClass('mt_primelink-export-wizard') || $('.mt_primelink-export-step-2').length){
      // Export page - use export template list
      refreshExportTemplateList();
    } else {
      // Import page - use import template list
      refreshTemplateList();
    
      // Check if template parameter is in URL and load it automatically
      const url = new URL(window.location.href);
      const templateParam = url.searchParams.get('template');
      if(templateParam){
        // If a template is explicitly requested via URL, prefer that.
        // Support both "template:NAME" and "import:ID" notations.
        setTimeout(async function(){
          try {
            let tplData = {};
            if (templateParam.startsWith('import:')) {
              // Load mapping from a previous import (used by Reload Import).
              const sourceId = parseInt(templateParam.replace('import:', ''), 10);
              if (sourceId > 0) {
                const res = await restPost('/import/templates/load', { import_id: sourceId });
                tplData = res.data || res || {};
              }
            } else {
              const templateName = templateParam.startsWith('template:') ? templateParam.replace('template:', '') : templateParam;
              const res = await restPost('/import/templates/load', { name: templateName });
              
              // Check if response indicates template not found
              if(res && res.message && (res.message.includes('not found') || res.message.includes('Template not found'))){
                showToastOrAlert('error', 'Failed to load template: ' + res.message, 'Load');
                // Remove template parameter from URL even on error
                url.searchParams.delete('template');
                history.replaceState({}, '', url.toString());
                return;
              }
              
              tplData = res.data || res || {};
              
              // Select the template in dropdown (with template: prefix for consistency)
              if (templateName) {
                $('#mt_primelink-load-template').val('template:' + templateName);
              }
            }
            
            // Only apply if we have actual mapping data
            if (tplData && (tplData.mapping || Object.keys(tplData).length > 0)) {
              applyTemplateToUI(tplData);
              showToastOrAlert('success', 'Template loaded automatically.', 'Load');
            } else {
              debugConsole.warn('Template data is empty:', tplData);
            }
            
            // Remove template parameter from URL
            url.searchParams.delete('template');
            history.replaceState({}, '', url.toString());
          } catch(e){
            debugConsole.error('Failed to load template:', e);
            // Handle 404 responses that contain error messages
            if(e.status === 404 && e.json && e.json.message){
              showToastOrAlert('error', 'Failed to load template: ' + e.json.message, 'Load');
            } else {
              showToastOrAlert('error', 'Failed to load template: ' + (e.json?.message || e.message || 'Unknown error'), 'Load');
            }
            // Remove template parameter from URL even on error
            url.searchParams.delete('template');
            history.replaceState({}, '', url.toString());
          }
        }, 800); // Increased delay to ensure UI is fully ready
      } else {
        // No explicit template in URL: auto-load mapping from current import draft (step 5 state).
        // This ensures that reloaded imports (and revisited imports) open with their saved mapping.
        setTimeout(async function(){
          try {
            const importId = currentImportId() || parseInt($('.mt_primelink-import-step-5').data('import-id'), 10) || 0;
            if(!importId){ return; }
            const res = await restPost('/import/templates/load', { import_id: importId });
            const tplData = res.data || res || {};
            if (tplData && (tplData.mapping || Object.keys(tplData).length > 0)) {
              applyTemplateToUI(tplData);
            }
          } catch(e){
            // Silent fail – mapping auto-load is a UX enhancement, not critical.
            debugConsole.warn('Auto-load of import mapping failed:', e);
          }
        }, 800);
      }
    }
  }
  if($('.mt_primelink-map-slot').length){
    syncSlotBadges();
  }
  toggleVariationPanel();
});

// -----------------------------
// Step 6 - Import Options
// -----------------------------

function collectStep6State(){
  const updateMode = $('input[name="mt_primelink-update-mode"]:checked').val() || 'all';
  const updateFields = [];
  $('.mt_primelink-update-field:checked').each(function(){
    updateFields.push($(this).val());
  });

  // Incremental import fields
  const incrementalFields = [];
  $('.mt_primelink-incremental-field:checked').each(function(){
    incrementalFields.push($(this).val());
  });

  // Duplicate detection fields
  const duplicateFields = [];
  $('.mt_primelink-duplicate-field:checked').each(function(){
    duplicateFields.push($(this).val());
  });

  return {
    match_existing: $('#mt_primelink-match-existing').val() || 'title',
    duplicate_detection: {
      enabled: $('#mt_primelink-duplicate-strategy').val() !== 'single',
      strategy: $('#mt_primelink-duplicate-strategy').val() || 'single',
      fields: duplicateFields.length > 0 ? duplicateFields : ['sku'],
      require_all: $('#mt_primelink-duplicate-require-all').is(':checked'),
      fuzzy_threshold: parseFloat($('#mt_primelink-fuzzy-threshold').val() || $('#mt_primelink-hybrid-fuzzy-threshold').val() || '85') / 100
    },
    update_mode: updateMode,
    update_fields: updateFields,
    use_first_gallery_as_featured: $('#mt_primelink-use-first-gallery-as-featured').is(':checked'),
    speed_optimization: $('#mt_primelink-speed-optimization').is(':checked'),
    iterative_processing: $('#mt_primelink-iterative-processing').is(':checked'),
    iteration_size: parseInt($('#mt_primelink-iteration-size').val(), 10) || 20,
    incremental: {
      enabled: $('#mt_primelink-incremental-enabled').is(':checked'),
      mode: $('#mt_primelink-incremental-mode').val() || 'all',
      fields: incrementalFields,
      skip_unchanged: $('#mt_primelink-incremental-skip-unchanged').is(':checked')
    },
    test_mode: {
      enabled: $('#mt_primelink-test-mode').is(':checked'),
      limit: parseInt($('#mt_primelink-test-limit').val()) || 10,
      validate_required: $('#mt_primelink-test-validate-required').is(':checked'),
      validate_format: $('#mt_primelink-test-validate-format').is(':checked'),
      check_duplicates: $('#mt_primelink-test-check-duplicates').is(':checked'),
      skip_unchanged: $('#mt_primelink-test-skip-unchanged').is(':checked')
    },
    schedule: {
      enabled: $('#mt_primelink-enable-schedule').is(':checked'),
      name: ($('#mt_primelink-schedule-name').val() || '').trim(),
      type: $('#mt_primelink-schedule-type').val() || 'preset',
      interval: $('#mt_primelink-schedule-interval').val() || 'daily',
      start_time: $('#mt_primelink-schedule-start').val() || '',
      cron: $('#mt_primelink-schedule-cron').val() || '',
      timezone: $('#mt_primelink-schedule-timezone').val() || '',
      start_date: $('#mt_primelink-schedule-start-date').val() || '',
      end_date: $('#mt_primelink-schedule-end-date').val() || '',
      max_runs: $('#mt_primelink-schedule-max-runs').val() || '',
      retry_failure: $('#mt_primelink-schedule-retry-failure').is(':checked'),
      retry_attempts: parseInt($('#mt_primelink-schedule-retry-attempts').val()) || 3,
      retry_delay: parseInt($('#mt_primelink-schedule-retry-delay').val()) || 30,
      pause_on_error: $('#mt_primelink-schedule-pause-on-error').is(':checked'),
      weekday_filter: $('#mt_primelink-schedule-weekday-filter').val() || '',
      weekly_days: (function(){
        const days = [];
        $('.mt_primelink-schedule-day:checked').each(function(){
          days.push(parseInt($(this).val()));
        });
        return days;
      })(),
      monthly_days: $('#mt_primelink-schedule-month-days').val() || '',
      time_slots: (function(){
        const slots = [];
        $('.mt_primelink-schedule-time-slot').each(function(){
          const time = $(this).val();
          if(time) slots.push(time);
        });
        if(!slots.length){
          const startTime = $('#mt_primelink-schedule-start').val();
          if(startTime) slots.push(startTime);
        }
        return slots;
      })(),
      email_attachment: $('#mt_primelink-email-attachment').is(':checked')
    },
    notify_override: {
      enabled: $('#mt_primelink-notify-override').is(':checked'),
      email: ($('#mt_primelink-notify-email-override').val() || '').trim(),
      tpl_finish: $('#mt_primelink-notify-finish-override').val() || '',
      tpl_error: $('#mt_primelink-notify-error-override').val() || ''
    }
  };
}

async function saveStep6(){
  const importId = currentImportId();
  const data = collectStep6State();

  await restPost('/import/save-step', {
    import_id: importId,
    step: 6,
    data: data
  });

  return data;
}

function applyStep6State(data, defaultSpeedOpt){
  data = data || {};
  defaultSpeedOpt = defaultSpeedOpt !== undefined ? defaultSpeedOpt : false;

  // Duplicate detection
  const dupConfig = data.duplicate_detection || {};
  const strategy = dupConfig.strategy || 'single';
  $('#mt_primelink-duplicate-strategy').val(strategy);
  
  // Show/hide strategy-specific options
  $('.mt_primelink-duplicate-strategy-wrap').hide();
  if(strategy === 'single'){
    $('#mt_primelink-duplicate-single-wrap').show();
  } else if(strategy === 'multiple'){
    $('#mt_primelink-duplicate-multiple-wrap').show();
  } else if(strategy === 'fuzzy'){
    $('#mt_primelink-duplicate-fuzzy-wrap').show();
  } else if(strategy === 'hybrid'){
    $('#mt_primelink-duplicate-hybrid-wrap').show();
  }

  // Set fields
  $('.mt_primelink-duplicate-field').prop('checked', false);
  if(Array.isArray(dupConfig.fields) && dupConfig.fields.length){
    dupConfig.fields.forEach(v => {
      $('.mt_primelink-duplicate-field[value="'+v+'"]').prop('checked', true);
    });
  } else {
    // Defaults
    if(strategy === 'single'){
      $('.mt_primelink-duplicate-field[value="sku"]').prop('checked', true);
    } else {
      $('.mt_primelink-duplicate-field[value="sku"]').prop('checked', true);
      if(strategy === 'fuzzy' || strategy === 'hybrid'){
        $('.mt_primelink-duplicate-field[value="title"]').prop('checked', true);
      }
    }
  }

  // Set other duplicate detection options
  $('#mt_primelink-duplicate-require-all').prop('checked', !!dupConfig.require_all);
  if(dupConfig.fuzzy_threshold !== undefined){
    const threshold = Math.round((dupConfig.fuzzy_threshold || 0.85) * 100);
    $('#mt_primelink-fuzzy-threshold').val(threshold);
    $('#mt_primelink-hybrid-fuzzy-threshold').val(threshold);
  }

  // Event handler for strategy change
  $('#mt_primelink-duplicate-strategy').off('change').on('change', function(){
    const strategy = $(this).val();
    $('.mt_primelink-duplicate-strategy-wrap').hide();
    if(strategy === 'single'){
      $('#mt_primelink-duplicate-single-wrap').show();
    } else if(strategy === 'multiple'){
      $('#mt_primelink-duplicate-multiple-wrap').show();
    } else if(strategy === 'fuzzy'){
      $('#mt_primelink-duplicate-fuzzy-wrap').show();
    } else if(strategy === 'hybrid'){
      $('#mt_primelink-duplicate-hybrid-wrap').show();
    }
  });

  // Trigger change to show correct wrap on load
  $('#mt_primelink-duplicate-strategy').trigger('change');

  // Legacy match_existing (for backward compatibility)
  $('#mt_primelink-match-existing').val(data.match_existing || 'title');

  const updateMode = data.update_mode || 'all';
  $('input[name="mt_primelink-update-mode"][value="'+updateMode+'"]').prop('checked', true);

  // show/hide choose fields
  if(updateMode === 'choose'){
    $('#mt_primelink-update-choose-wrap').show();
  } else {
    $('#mt_primelink-update-choose-wrap').hide();
  }

  const updateFields = Array.isArray(data.update_fields) ? data.update_fields : [];
  $('.mt_primelink-update-field').prop('checked', false);
  if(updateFields.length){
    updateFields.forEach(v=>{
      $('.mt_primelink-update-field[value="'+v+'"]').prop('checked', true);
    });
  } else {
    // default all on
    $('.mt_primelink-update-field').prop('checked', true);
  }

  $('#mt_primelink-use-first-gallery-as-featured').prop('checked', !!data.use_first_gallery_as_featured);
  
  // Speed optimization: use data.speed_optimization if set, otherwise use default_speed_opt from settings
  let speedOptimization = data.speed_optimization;
  if (speedOptimization === undefined || speedOptimization === null) {
    // Use default from settings if not set in import data
    speedOptimization = defaultSpeedOpt;
  }
  $('#mt_primelink-speed-optimization').prop('checked', !!speedOptimization);
  
  $('#mt_primelink-iterative-processing').prop('checked', data.iterative_processing !== false);
  $('#mt_primelink-iteration-size').val(data.iteration_size || 20);

  // Incremental import
  const incremental = data.incremental || {};
  $('#mt_primelink-incremental-enabled').prop('checked', !!incremental.enabled);
  if(incremental.enabled){
    $('#mt_primelink-incremental-options').show();
  } else {
    $('#mt_primelink-incremental-options').hide();
  }
  $('#mt_primelink-incremental-mode').val(incremental.mode || 'all');
  $('#mt_primelink-incremental-skip-unchanged').prop('checked', !!incremental.skip_unchanged);
  
  // Show/hide custom fields based on mode
  if(incremental.mode === 'custom'){
    $('#mt_primelink-incremental-fields-wrap').show();
  } else {
    $('#mt_primelink-incremental-fields-wrap').hide();
  }
  
  const incrementalFields = Array.isArray(incremental.fields) ? incremental.fields : [];
  $('.mt_primelink-incremental-field').prop('checked', false);
  if(incrementalFields.length){
    incrementalFields.forEach(v=>{
      $('.mt_primelink-incremental-field[value="'+v+'"]').prop('checked', true);
    });
  } else {
    // default: title and product_data
    $('.mt_primelink-incremental-field[value="title"]').prop('checked', true);
    $('.mt_primelink-incremental-field[value="product_data"]').prop('checked', true);
  }

  // Test mode
  const testMode = data.test_mode || {};
  $('#mt_primelink-test-mode').prop('checked', !!testMode.enabled);
  if(testMode.enabled){
    $('#mt_primelink-test-mode-options').show();
    $('#mt_primelink-test-limit').val(testMode.limit || 10);
    $('#mt_primelink-test-validate-required').prop('checked', testMode.validate_required !== false);
    $('#mt_primelink-test-validate-format').prop('checked', testMode.validate_format !== false);
    $('#mt_primelink-test-check-duplicates').prop('checked', testMode.check_duplicates !== false);
    $('#mt_primelink-test-skip-unchanged').prop('checked', !!testMode.skip_unchanged);
  } else {
    $('#mt_primelink-test-mode-options').hide();
  }

  const sched = data.schedule || {};
  $('#mt_primelink-enable-schedule').prop('checked', !!sched.enabled);

  if(sched.enabled){
    $('#mt_primelink-schedule-wrap').show();
  } else {
    $('#mt_primelink-schedule-wrap').hide();
  }

  $('#mt_primelink-schedule-name').val(sched.name || '');
  $('#mt_primelink-schedule-type').val(sched.type || 'preset');
  $('#mt_primelink-schedule-interval').val(sched.interval || 'daily');
  $('#mt_primelink-schedule-start').val(sched.start_time || '00:00');
  $('#mt_primelink-schedule-cron').val(sched.cron || '');
  $('#mt_primelink-schedule-timezone').val(sched.timezone || '');
  $('#mt_primelink-schedule-start-date').val(sched.start_date || '');
  $('#mt_primelink-schedule-end-date').val(sched.end_date || '');
  $('#mt_primelink-schedule-max-runs').val(sched.max_runs || '');
  $('#mt_primelink-schedule-retry-failure').prop('checked', !!sched.retry_failure);
  $('#mt_primelink-schedule-retry-attempts').val(sched.retry_attempts || 3);
  $('#mt_primelink-schedule-retry-delay').val(sched.retry_delay || 30);
  $('#mt_primelink-schedule-pause-on-error').prop('checked', !!sched.pause_on_error);
  $('#mt_primelink-schedule-weekday-filter').val(sched.weekday_filter || '');
  
  // Weekly days
  if(sched.weekly_days && Array.isArray(sched.weekly_days)){
    $('.mt_primelink-schedule-day').prop('checked', false);
    sched.weekly_days.forEach(day => {
      $('.mt_primelink-schedule-day[value="'+day+'"]').prop('checked', true);
    });
  }
  
  // Monthly days
  $('#mt_primelink-schedule-month-days').val(sched.monthly_days || '');
  
  // Multiple time slots
  const timesWrap = $('#mt_primelink-schedule-times-wrap').empty();
  if(sched.time_slots && Array.isArray(sched.time_slots) && sched.time_slots.length > 0){
    sched.time_slots.forEach((time, idx) => {
      timesWrap.append(renderScheduleTimeRow(time, idx > 0));
    });
  } else {
    timesWrap.append(renderScheduleTimeRow('00:00', false));
  }
  
  // Show/hide schedule type sections
  updateScheduleTypeUI();
  
  $('#mt_primelink-email-attachment').prop('checked', !!sched.email_attachment);

  const notify = data.notify_override || {};
  $('#mt_primelink-notify-override').prop('checked', !!notify.enabled);
  if(notify.enabled){
    $('#mt_primelink-notify-override-wrap').show();
  } else {
    $('#mt_primelink-notify-override-wrap').hide();
  }
  $('#mt_primelink-notify-email-override').val(notify.email || '');
  $('#mt_primelink-notify-finish-override').val(notify.tpl_finish || '');
  $('#mt_primelink-notify-error-override').val(notify.tpl_error || '');
}

async function loadStep6(){
  const importId = currentImportId();

  try {
    const res = await restPost('/import/save-step', {
      import_id: importId,
      step: 6
    });
    // If backend returns nothing on GET-like call, ignore.
    if(res && res.data){
      // Load settings to get default_speed_opt
      let defaultSpeedOpt = false;
      try {
        const settingsRes = await restGet('/settings?ts=' + Date.now());
        defaultSpeedOpt = !!settingsRes.default_speed_opt;
      } catch(e) {
        // If settings load fails, default to false
      }
      applyStep6State(res.data, defaultSpeedOpt);
    }
  } catch(e){
    // ignore
  }
}

// UI logic
$(document).on('change', 'input[name="mt_primelink-update-mode"]', function(){
  const v = $(this).val();
  if(v === 'choose'){
    $('#mt_primelink-update-choose-wrap').slideDown(120);
  } else {
    $('#mt_primelink-update-choose-wrap').slideUp(120);
  }
  saveStep6();
});

function renderScheduleTimeRow(time, showRemove){
  return `
    <div class="mt_primelink-schedule-time-row" style="display:grid;grid-template-columns:1fr auto;gap:8px;margin-bottom:4px;">
      <input type="time" class="mt_primelink-schedule-time-slot" value="${time || '00:00'}" />
      <button type="button" class="button mt_primelink-schedule-time-remove" ${showRemove ? '' : 'style="display:none;"'}>Remove</button>
    </div>
  `;
}

function updateScheduleTypeUI(){
  const type = $('#mt_primelink-schedule-type').val();
  $('.mt_primelink-schedule-type-wrap').hide();
  
  if(type === 'preset'){
    $('#mt_primelink-schedule-preset-wrap').show();
    const interval = $('#mt_primelink-schedule-interval').val();
    if(interval === 'weekly'){
      $('#mt_primelink-schedule-weekly-options').show();
    } else {
      $('#mt_primelink-schedule-weekly-options').hide();
    }
    if(interval === 'monthly'){
      $('#mt_primelink-schedule-monthly-options').show();
    } else {
      $('#mt_primelink-schedule-monthly-options').hide();
    }
  } else if(type === 'custom'){
    $('#mt_primelink-schedule-custom-wrap').show();
  } else if(type === 'advanced'){
    $('#mt_primelink-schedule-advanced-wrap').show();
  }
}

// Test Mode
$(document).on('change', '#mt_primelink-test-mode', function(){
  if($(this).is(':checked')){
    $('#mt_primelink-test-mode-options').slideDown(120);
  } else {
    $('#mt_primelink-test-mode-options').slideUp(120);
    $('#mt_primelink-test-results').hide();
  }
  saveStep6();
});

$(document).on('click', '#mt_primelink-run-test-import', async function(){
  const importId = currentImportId();
  if(!importId) {
    alert('No import ID found.');
    return;
  }

  const $btn = $(this);
  const originalText = $btn.text();
  $btn.prop('disabled', true).text('Running test...');

  const options = {
    limit: parseInt($('#mt_primelink-test-limit').val()) || 10,
    validate_required: $('#mt_primelink-test-validate-required').is(':checked'),
    validate_format: $('#mt_primelink-test-validate-format').is(':checked'),
    check_duplicates: $('#mt_primelink-test-check-duplicates').is(':checked'),
    skip_unchanged: $('#mt_primelink-test-skip-unchanged').is(':checked')
  };

  try {
    const results = await restPost('/import/dry-run', {
      import_id: importId,
      ...options
    });

    if(!results.success){
      alert(results.error || 'Test import failed.');
      $btn.prop('disabled', false).text(originalText);
      return;
    }

    // Display results
    displayTestResults(results);
    $('#mt_primelink-test-results').slideDown(200);
    
  } catch(e){
    alert(e.json?.message || 'Failed to run test import.');
  } finally {
    $btn.prop('disabled', false).text(originalText);
  }
});

function displayTestResults(results){
  const container = $('#mt_primelink-test-results-content').empty();
  
  // Summary
  const summary = `
    <div style="margin-bottom:16px;padding:12px;background:rgba(255,255,255,0.05);border-radius:4px;">
      <h5 style="margin-top:0;">Summary</h5>
      <div style="display:grid;grid-template-columns:repeat(2, 1fr);gap:8px;">
        <div><strong>Total Records:</strong> ${results.total}</div>
        <div><strong>Processed:</strong> ${results.processed}</div>
        <div style="color:#4caf50;"><strong>Would Create:</strong> ${results.would_create}</div>
        <div style="color:#ff9800;"><strong>Would Update:</strong> ${results.would_update}</div>
        <div style="color:#9e9e9e;"><strong>Would Skip:</strong> ${results.would_skip}</div>
        <div style="color:#f44336;"><strong>Errors:</strong> ${results.errors}</div>
        <div style="color:#ff9800;"><strong>Warnings:</strong> ${results.warnings}</div>
      </div>
    </div>
  `;
  container.append(summary);

  // Errors
  if(results.validation.errors && results.validation.errors.length > 0){
    let errorsHtml = '<div style="margin-bottom:12px;"><h5 style="color:#f44336;margin-top:0;">Errors</h5><div style="max-height:200px;overflow-y:auto;">';
    results.validation.errors.slice(0, 20).forEach(err => {
      errorsHtml += `<div style="padding:6px;margin-bottom:4px;background:rgba(244,67,54,0.1);border-left:3px solid #f44336;">
        <strong>Row ${err.row}</strong> - ${err.field}: ${err.message}
      </div>`;
    });
    if(results.validation.errors.length > 20){
      errorsHtml += `<div style="padding:6px;color:#9e9e9e;">... and ${results.validation.errors.length - 20} more errors</div>`;
    }
    errorsHtml += '</div></div>';
    container.append(errorsHtml);
  }

  // Warnings
  if(results.validation.warnings && results.validation.warnings.length > 0){
    let warningsHtml = '<div style="margin-bottom:12px;"><h5 style="color:#ff9800;margin-top:0;">Warnings</h5><div style="max-height:200px;overflow-y:auto;">';
    results.validation.warnings.slice(0, 20).forEach(warn => {
      warningsHtml += `<div style="padding:6px;margin-bottom:4px;background:rgba(255,152,0,0.1);border-left:3px solid #ff9800;">
        <strong>Row ${warn.row}</strong> - ${warn.field}: ${warn.message}
      </div>`;
    });
    if(results.validation.warnings.length > 20){
      warningsHtml += `<div style="padding:6px;color:#9e9e9e;">... and ${results.validation.warnings.length - 20} more warnings</div>`;
    }
    warningsHtml += '</div></div>';
    container.append(warningsHtml);
  }

  // Sample items
  if(results.sample_items && results.sample_items.length > 0){
    let samplesHtml = '<div><h5 style="margin-top:0;">Sample Items (First 5)</h5><div style="max-height:300px;overflow-y:auto;">';
    results.sample_items.forEach(item => {
      const actionColor = item.action === 'create' ? '#4caf50' : '#ff9800';
      samplesHtml += `<div style="padding:8px;margin-bottom:6px;background:rgba(255,255,255,0.05);border-radius:4px;">
        <div style="display:flex;justify-content:space-between;align-items:center;">
          <div><strong>Row ${item.row}</strong> - <span style="color:${actionColor};">${item.action.toUpperCase()}</span></div>
          ${item.item_id ? `<div style="color:#9e9e9e;">Existing ID: ${item.item_id}</div>` : ''}
        </div>
        <div style="margin-top:4px;font-size:12px;color:#9e9e9e;">
          ${item.mapped.post?.post_title || item.mapped.product?.sku || item.mapped.order?.order_number || 'N/A'}
        </div>
      </div>`;
    });
    samplesHtml += '</div></div>';
    container.append(samplesHtml);
  }

  // Success message
  if(results.errors === 0 && results.warnings === 0){
    container.prepend('<div style="padding:12px;background:rgba(76,175,80,0.2);border-left:3px solid #4caf50;margin-bottom:12px;border-radius:4px;"><strong style="color:#4caf50;">&#10003; Test completed successfully! No errors or warnings found.</strong></div>');
  } else if(results.errors === 0){
    container.prepend('<div style="padding:12px;background:rgba(255,152,0,0.2);border-left:3px solid #ff9800;margin-bottom:12px;border-radius:4px;"><strong style="color:#ff9800;">&#9888; Test completed with warnings. Review warnings before running actual import.</strong></div>');
  } else {
    container.prepend('<div style="padding:12px;background:rgba(244,67,54,0.2);border-left:3px solid #f44336;margin-bottom:12px;border-radius:4px;"><strong style="color:#f44336;">&#10007; Test completed with errors. Please fix errors before running actual import.</strong></div>');
  }
}

$(document).on('change', '#mt_primelink-enable-schedule', function(){
  if($(this).is(':checked')){
    $('#mt_primelink-schedule-wrap').slideDown(120);
  } else {
    $('#mt_primelink-schedule-wrap').slideUp(120);
  }
  saveStep6();
});

$(document).on('change', '#mt_primelink-schedule-type', function(){
  updateScheduleTypeUI();
  saveStep6();
});

$(document).on('change', '#mt_primelink-schedule-interval', function(){
  updateScheduleTypeUI();
  saveStep6();
});

$(document).on('change', '#mt_primelink-schedule-retry-failure', function(){
  if($(this).is(':checked')){
    $('#mt_primelink-schedule-retry-options').slideDown(120);
  } else {
    $('#mt_primelink-schedule-retry-options').slideUp(120);
  }
  saveStep6();
});

$(document).on('click', '#mt_primelink-schedule-time-add', function(){
  const timesWrap = $('#mt_primelink-schedule-times-wrap');
  const rows = timesWrap.find('.mt_primelink-schedule-time-row');
  rows.find('.mt_primelink-schedule-time-remove').show();
  timesWrap.append(renderScheduleTimeRow('00:00', true));
  saveStep6();
});

$(document).on('click', '.mt_primelink-schedule-time-remove', function(){
  const rows = $('#mt_primelink-schedule-times-wrap').find('.mt_primelink-schedule-time-row');
  if(rows.length > 1){
    $(this).closest('.mt_primelink-schedule-time-row').remove();
    const remaining = $('#mt_primelink-schedule-times-wrap').find('.mt_primelink-schedule-time-row');
    if(remaining.length === 1){
      remaining.find('.mt_primelink-schedule-time-remove').hide();
    }
    saveStep6();
  }
});
$(document).on('change', '#mt_primelink-notify-override', function(){
  if($(this).is(':checked')){
    $('#mt_primelink-notify-override-wrap').slideDown(120);
  } else {
    $('#mt_primelink-notify-override-wrap').slideUp(120);
  }
  saveStep6();
});

// Incremental import UI handlers
$(document).on('change', '#mt_primelink-incremental-enabled', function(){
  if($(this).is(':checked')){
    $('#mt_primelink-incremental-options').slideDown(120);
  } else {
    $('#mt_primelink-incremental-options').slideUp(120);
  }
  saveStep6();
});

$(document).on('change', '#mt_primelink-incremental-mode', function(){
  const mode = $(this).val();
  if(mode === 'custom'){
    $('#mt_primelink-incremental-fields-wrap').slideDown(120);
  } else {
    $('#mt_primelink-incremental-fields-wrap').slideUp(120);
  }
  saveStep6();
});

// autosave for all step6 inputs
$(document).on('change blur', '.mt_primelink-import-step-6 input, .mt_primelink-import-step-6 select, .mt_primelink-import-step-6 textarea', function(){
  saveStep6();
});

// -----------------------------
// Step 7 - Review Summary
// -----------------------------

function shortPath(p){
  if(!p) return '';
  const clean = String(p);
  if(clean.length <= 48) return clean;
  const parts = clean.split(/[/\\\\]/);
  if(parts.length < 3) return clean;
  return parts[0] + '/.../' + parts[parts.length-1];
}

function renderSummaryRows(rows, emptyText){
  const html = rows
    .filter(r => r && r.value !== undefined && r.value !== null && r.value !== '')
    .map(r => (
      '<div class="mt_primelink-summary-row">' +
        '<span class="mt_primelink-summary-label">'+escapeHtml(r.label)+'</span>' +
        '<span class="mt_primelink-summary-value">'+escapeHtml(String(r.value))+'</span>' +
      '</div>'
    )).join('');
  return html || '<div class="mt_primelink-empty">'+escapeHtml(emptyText || 'No data yet.')+'</div>';
}

function renderSummaryList(items, emptyText){
  if(!items || !items.length){
    return '<div class="mt_primelink-empty">'+escapeHtml(emptyText || 'No data yet.')+'</div>';
  }
  return '<ul class="mt_primelink-summary-list">' + items.map(item =>
    {
      const val = (item.value === undefined || item.value === null) ? '' : String(item.value);
      return '<li><span class="mt_primelink-summary-label">'+escapeHtml(item.label)+'</span>' +
             '<span class="mt_primelink-summary-value">'+escapeHtml(val)+'</span></li>';
    }
  ).join('') + '</ul>';
}

function summarizeFile(fileInfo){
  if(!fileInfo || !Object.keys(fileInfo).length){
    return '<div class="mt_primelink-empty">No file selected yet.</div>';
  }

  const badge = fileInfo.type ? '<span class="mt_primelink-summary-pill mt_primelink-pill-ghost">'+escapeHtml(String(fileInfo.type).toUpperCase())+'</span>' : '';

  const meta = [
    { label: 'Source', value: fileInfo.source || 'Unknown' },
    { label: 'Stored path', value: shortPath(fileInfo.path) },
    { label: 'Uploaded', value: fileInfo.uploaded_at || '' }
  ];

  return (
    '<div class="mt_primelink-summary-card">' +
      '<div class="mt_primelink-summary-top">' +
        '<span class="mt_primelink-summary-eyebrow">Data source</span>' +
        badge +
      '</div>' +
      '<div class="mt_primelink-summary-lead">'+escapeHtml(fileInfo.original_name || 'File attached')+'</div>' +
      '<div class="mt_primelink-summary-meta-grid">' +
        meta.map(m =>
          '<div class="mt_primelink-summary-meta">' +
            '<span class="mt_primelink-summary-label">'+escapeHtml(m.label)+'</span>' +
            '<span class="mt_primelink-summary-value">'+escapeHtml(String(m.value || ''))+'</span>' +
          '</div>'
        ).join('') +
      '</div>' +
    '</div>'
  );
}

function summarizeOptions(options){
  const modeLabels = {
    new_update: 'Import new items & update existing',
    new_only: 'Import new items only',
    update_only: 'Update existing items only'
  };
  const matchLabels = {
    title: 'Title',
    content: 'Content',
    slug: 'Slug',
    post_meta: 'Post Meta',
    sku: 'SKU',
    post_id: 'Post ID'
  };
  const updateModeLabels = {
    all: 'Update all data',
    price_stock: 'Update price & stock only',
    choose: 'Update selected fields only'
  };
  const updateFieldLabels = {
    title: 'Title & Content',
    product_data: 'Product Data',
    images: 'Images',
    yoast: 'Yoast SEO',
    custom_fields: 'Custom Fields',
    taxonomies: 'Taxonomies'
  };
  const intervalLabels = {
    hourly: 'Hourly',
    twicedaily: 'Twice Daily',
    daily: 'Daily',
    weekly: 'Weekly',
    monthly: 'Monthly'
  };

  const step3 = options.step3 || {};
  const step6 = options.step6 || {};

  const rows = [];

  rows.push({
    label: 'Import mode',
    value: modeLabels[step3.import_mode] || step3.import_mode || 'Not set'
  });

  rows.push({
    label: 'Match existing by',
    value: matchLabels[step6.match_existing] || step6.match_existing || 'Title'
  });

  rows.push({
    label: 'Update strategy',
    value: updateModeLabels[step6.update_mode] || updateModeLabels.all
  });

  rows.push({
    label: 'Speed optimization',
    value: step6.speed_optimization ? 'Enabled' : 'Disabled'
  });

  rows.push({
    label: 'Iterative processing',
    value: step6.iterative_processing
      ? 'Enabled (' + (step6.iteration_size || 20) + ' per batch)'
      : 'Disabled'
  });

  if(step6.update_mode === 'choose'){
    const fields = (step6.update_fields || []).map(f => updateFieldLabels[f] || f);
    rows.push({
      label: 'Fields to update',
      value: fields.length ? fields.join(', ') : 'None selected'
    });
  }

  const schedule = step6.schedule || {};
  const scheduleRows = [
    { label: 'Scheduling', value: schedule.enabled ? 'Enabled' : 'Disabled' }
  ];
  if(schedule.enabled){
    scheduleRows.push({ label: 'Name', value: schedule.name || 'Background import' });
    scheduleRows.push({ label: 'Interval', value: intervalLabels[schedule.interval] || schedule.interval || 'Daily' });
    if(schedule.start_time){
      scheduleRows.push({ label: 'Start time', value: schedule.start_time });
    }
    scheduleRows.push({ label: 'Email attachment', value: schedule.email_attachment ? 'Yes' : 'No' });
  }

  const notify = step6.notify_override || {};
  const notifyRows = [
    { label: 'Notifications', value: notify.enabled ? 'Custom (this import)' : 'Global defaults' }
  ];
  if(notify.enabled){
    notifyRows.push({ label: 'Notify email', value: notify.email || '(not set)' });
    if(notify.tpl_finish){
      notifyRows.push({ label: 'Finish template', value: (notify.tpl_finish || '').slice(0, 120) + (notify.tpl_finish.length > 120 ? '&#8230;' : '') });
    }
    if(notify.tpl_error){
      notifyRows.push({ label: 'Error template', value: (notify.tpl_error || '').slice(0, 120) + (notify.tpl_error.length > 120 ? '&#8230;' : '') });
    }
  }

  return (
    '<div class="mt_primelink-summary-card">' +
      '<div class="mt_primelink-summary-top"><span class="mt_primelink-summary-eyebrow">Options</span></div>' +
      '<div class="mt_primelink-pill-row">' +
        '<span class="mt_primelink-summary-pill">'+escapeHtml(modeLabels[step3.import_mode] || 'Mode not set')+'</span>' +
        '<span class="mt_primelink-summary-pill">'+escapeHtml('Match: ' + (matchLabels[step6.match_existing] || 'Title'))+'</span>' +
        '<span class="mt_primelink-summary-pill">'+escapeHtml('Update: ' + (updateModeLabels[step6.update_mode] || updateModeLabels.all))+'</span>' +
      '</div>' +
      '<div class="mt_primelink-summary-stack">' +
        renderSummaryRows(rows, 'No options selected yet.') +
      '</div>' +
      '<div class="mt_primelink-summary-divider"></div>' +
      '<div class="mt_primelink-summary-subtitle">Scheduling</div>' +
      renderSummaryRows(scheduleRows, 'Scheduling not enabled.') +
      '<div class="mt_primelink-summary-divider"></div>' +
      '<div class="mt_primelink-summary-subtitle">Notifications</div>' +
      renderSummaryRows(notifyRows, 'Using global defaults.') +
    '</div>'
  );
}

function summarizeMapping(mappingData){
  const mapping = mappingData.mapping || {};
  const custom = mappingData.custom_fields || [];
  const mapped = Object.entries(mapping).filter(([,cfg]) => cfg && cfg.source);

  const mappedList = mapped.map(([target, cfg]) => ({
    label: target,
    value: cfg.source
  }));

  const attrList = mapped
    .filter(([target]) => target.indexOf('attribute:') === 0)
    .map(([target, cfg]) => ({
      label: target.replace(/^attribute:/, ''),
      value: cfg.source,
      variation: !!cfg.variation
    }));

  const customList = (custom || []).filter(cf => cf && cf.key).map(cf => ({
    label: cf.key,
    value: cf.source || cf.field || cf.value || ''
  }));

  // Critical fields: Title / SKU presence (only for products)
  let criticalBadges = [];
  if(!document.body.classList.contains('mt_primelink-import-orders')){
    const criticalDefs = [
      { label: 'Title', keys: ['title','post_title','name'] },
      { label: 'SKU',   keys: ['sku','product_sku'] }
    ];
    criticalBadges = criticalDefs.map(def => {
      const found = mapped.some(([target]) => def.keys.includes(String(target).toLowerCase()));
      return {
        label: def.label,
        ok: found
      };
    });
  } else {
    // For orders, check Order ID or Customer Email
    const orderCriticalDefs = [
      { label: 'Order ID', keys: ['order_id'] },
      { label: 'Customer Email', keys: ['customer_email'] }
    ];
    criticalBadges = orderCriticalDefs.map(def => {
      const found = mapped.some(([target]) => def.keys.includes(String(target).toLowerCase()));
      return {
        label: def.label,
        ok: found
      };
    });
  }

  let html = '<div class="mt_primelink-summary-stack">';
  html += '<div class="mt_primelink-summary-card">';
  html += '  <div class="mt_primelink-summary-top"><span class="mt_primelink-summary-eyebrow">Mapping</span></div>';
  html += '  <div class="mt_primelink-summary-stat-grid">';
  html += '    <div class="mt_primelink-summary-stat"><div class="mt_primelink-stat-value">'+mappedList.length+'</div><div class="mt_primelink-stat-label">Mapped targets</div></div>';
  if(!document.body.classList.contains('mt_primelink-import-orders')){
    html += '    <div class="mt_primelink-summary-stat"><div class="mt_primelink-stat-value">'+attrList.length+'</div><div class="mt_primelink-stat-label">Attributes</div></div>';
  }
  html += '    <div class="mt_primelink-summary-stat"><div class="mt_primelink-stat-value">'+customList.length+'</div><div class="mt_primelink-stat-label">Custom fields</div></div>';
  html += '  </div>';

  if(criticalBadges.length > 0){
    html += '<div class="mt_primelink-critical-badges">';
    criticalBadges.forEach(b=>{
      html += '<span class="mt_primelink-critical-badge '+(b.ok?'is-ok':'is-missing')+'">'+escapeHtml(b.label)+(b.ok?' mapped':' missing')+'</span>';
    });
    html += '</div>';
  }

  html += '  <div class="mt_primelink-summary-divider"></div>';
  html += '  <div class="mt_primelink-summary-subtitle">Targets</div>';
  html +=      renderSummaryList(mappedList, 'No target fields mapped yet.');

  // Attributes section (only for products)
  if(!document.body.classList.contains('mt_primelink-import-orders')){
    if(attrList.length){
      const attrsWithFlag = attrList.map(a => ({
        label: a.label + (a.variation ? ' (variations)' : ''),
        value: a.value
      }));
      html += '  <div class="mt_primelink-summary-subtitle">Attributes</div>';
      html +=        renderSummaryList(attrsWithFlag, 'No attributes mapped.');
      if(mappingData.create_variations){
        html += '<div class="mt_primelink-summary-note">Create variations: Enabled</div>';
      }
    }
  }

  if(customList.length){
    html += '  <div class="mt_primelink-summary-subtitle">Custom Fields</div>';
    html +=        renderSummaryList(customList, 'No custom fields added yet.');
  }

  // Product type (only for products)
  if(!document.body.classList.contains('mt_primelink-import-orders')){
    if(mappingData.product_type && mappingData.product_type !== 'mapped'){
      html += '<div class="mt_primelink-summary-note">Product type: '+escapeHtml(mappingData.product_type)+'</div>';
    }
  }
  html += '</div>';
  html += '</div>';

  return html;
}

async function buildSummary(){
  const importId = currentImportId();

  // Don't make request if no valid import_id
  if(!importId || importId === 0){
    return;
  }

  try {
    const res = await restPost('/import/get-draft', { import_id: importId });
    const draft = res.draft || {};
    const steps = draft.step_data || {};

    const fileInfo = steps[2]?.file || {};
    const options  = {
      step3: steps[3] || {},
      step4: steps[4] || {},
      step6: steps[6] || {}
    };
    const mapping  = steps[5] || {};

    $('#mt_primelink-summary-file').html(summarizeFile(fileInfo));
    $('#mt_primelink-summary-options').html(summarizeOptions(options));
    $('#mt_primelink-summary-mapping').html(summarizeMapping(mapping));

    // Warnings (no mapping / no images)
    const warnings = [];
    const mapped = mapping.mapping || mapping;
    const mappedCount = Object.values(mapped || {}).filter(cfg => cfg && cfg.source).length;
    // Nu afiÈ™a warnings pentru orders
    if(!document.body.classList.contains('mt_primelink-import-orders')) {
      if(!mappedCount){
        warnings.push('No fields mapped. Please map at least one field.');
      }
      const hasImage = Object.keys(mapped || {}).some(k => /image|img|gallery/i.test(k) && mapped[k]?.source);
      if(!hasImage){
        warnings.push('No image fields mapped or stored. Make sure products get images.');
      }
    }

    const $warn = $('#mt_primelink-summary-warnings');
    if($warn.length){
      if(warnings.length){
        $warn.html('<strong>Review warnings:</strong><ul>'+warnings.map(w=>'<li>'+escapeHtml(w)+'</li>').join('')+'</ul>');
        $warn.show();
      } else {
        $warn.hide().empty();
      }
    }

    // Critical missing list + link back to step 5
    const criticalMissing = [];
    const mappedTargets = Object.keys(mapped || {}).map(k => k.toLowerCase());
    // Nu afiÈ™a critical missing pentru Title/SKU la orders
    if(!document.body.classList.contains('mt_primelink-import-orders')) {
      const hasTitle = mappedTargets.some(k => ['title','post_title','name'].includes(k));
      if(!hasTitle) criticalMissing.push('Title missing');
      const hasSKU = mappedTargets.some(k => ['sku'].includes(k));
      if(!hasSKU) criticalMissing.push('SKU missing');
    } else {
      // For orders, check Order ID or Customer Email instead
      const hasOrderId = mappedTargets.some(k => ['order_id'].includes(k));
      const hasCustomerEmail = mappedTargets.some(k => ['customer_email'].includes(k));
      if(!hasOrderId && !hasCustomerEmail) {
        criticalMissing.push('Order ID or Customer Email missing');
      }
    }
    const $crit = $('#mt_primelink-critical-missing');
    if($crit.length){
      if(criticalMissing.length){
        $crit.html('<strong>Critical fields missing:</strong><ul>'+criticalMissing.map(w=>'<li>'+escapeHtml(w)+'</li>').join('')+'</ul>');
        $crit.show();
      } else {
        $crit.hide().empty();
      }
    }

    $('#mt_primelink-summary-loading').hide();
    $('#mt_primelink-summary-content').show();

  } catch(e){
    $('#mt_primelink-summary-loading').text('Summary unavailable.');
  }
}

// -----------------------------
// Step 8 - Runner UI
// -----------------------------

// Track previous progress for smooth interpolation
let previousProgress = 0;
let progressAnimationFrame = null;

// ETA calculation variables
let etaStartTime = null;
let etaLastUpdateTime = null;
let etaLastCurrent = 0;
let etaItemsPerSecond = 0;

/**
 * Format seconds into human-readable ETA string
 * @param {number} seconds - Total seconds
 * @returns {string} Formatted ETA (e.g., "2m 30s", "1h 15m", "45s")
 */
function formatETA(seconds) {
  if (!seconds || seconds <= 0 || !isFinite(seconds)) {
    return '--';
  }
  
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);
  
  if (hours > 0) {
    return hours + 'h ' + (minutes > 0 ? minutes + 'm' : '');
  } else if (minutes > 0) {
    return minutes + 'm ' + (secs > 0 ? secs + 's' : '');
  } else {
    return secs + 's';
  }
}

/**
 * Calculate ETA based on current progress and speed
 * @param {number} current - Current items processed
 * @param {number} total - Total items to process
 * @param {string} status - Current status
 * @returns {string} Formatted ETA string
 */
function calculateETA(current, total, status) {
  // Only calculate ETA when running
  if (status !== 'running' || total === 0 || current >= total) {
    return '--';
  }
  
  const now = Date.now();
  const remaining = total - current;
  
  // Initialize tracking on first run
  if (etaStartTime === null) {
    etaStartTime = now;
    etaLastUpdateTime = now;
    etaLastCurrent = current;
    etaItemsPerSecond = 0;
    // Show "Calculating..." for first few seconds
    return 'Calculating...';
  }
  
  // Update speed calculation (exponential moving average for smoother results)
  const timeDiff = (now - etaLastUpdateTime) / 1000; // seconds
  const itemsDiff = current - etaLastCurrent;
  
  // Update speed if we have progress and time has passed (minimum 0.3 seconds for accuracy)
  if (timeDiff >= 0.3 && itemsDiff >= 0) {
    if (itemsDiff > 0) {
      const currentSpeed = itemsDiff / timeDiff; // items per second
      // Use exponential moving average (70% old, 30% new) for smoother ETA
      if (etaItemsPerSecond > 0) {
        etaItemsPerSecond = etaItemsPerSecond * 0.7 + currentSpeed * 0.3;
      } else {
        // First calculation - use current speed directly
        etaItemsPerSecond = currentSpeed;
      }
      // Update tracking variables
      etaLastUpdateTime = now;
      etaLastCurrent = current;
    } else if (timeDiff >= 1) {
      // If no progress but time passed, still update tracking to avoid stale data
      etaLastUpdateTime = now;
    }
  }
  
  // Calculate ETA using current speed
  if (etaItemsPerSecond > 0) {
    const etaSeconds = remaining / etaItemsPerSecond;
    return formatETA(etaSeconds);
  }
  
  // Fallback: estimate based on elapsed time (if we have at least some progress)
  const elapsed = (now - etaStartTime) / 1000; // seconds
  if (current > 0 && elapsed >= 1) { // Wait at least 1 second before showing fallback estimate
    const avgSpeed = current / elapsed;
    if (avgSpeed > 0) {
      const etaSeconds = remaining / avgSpeed;
      return formatETA(etaSeconds);
    }
  }
  
  // Still calculating if we don't have enough data (show this immediately)
  return 'Calculating...';
}

function renderRunnerState(state){
  const total = state.total || 0;
  const current = state.current || 0;
  const progress = Math.max(0, Math.min(100, state.progress || 0));
  const status = state.status || 'idle';
  const statusText = String(status).charAt(0).toUpperCase() + String(status).slice(1);
  
  // Reset ETA tracking when status changes
  if (status !== 'running' && etaStartTime !== null) {
    etaStartTime = null;
    etaLastUpdateTime = null;
    etaLastCurrent = 0;
    etaItemsPerSecond = 0;
  }

  // Smooth progress bar animation
  const $progressBar = $('.mt_primelink-progress-bar__inner');
  const targetProgress = progress;
  
  // Cancel any ongoing animation
  if (progressAnimationFrame) {
    cancelAnimationFrame(progressAnimationFrame);
  }
  
  // Smooth interpolation for progress bar using previousProgress variable
  const animateProgress = () => {
    const currentProgress = previousProgress;
    const diff = targetProgress - currentProgress;
    
    if (Math.abs(diff) > 0.1) {
      // Smooth interpolation (ease-out) - 20% of difference per frame
      const step = diff * 0.2;
      const newProgress = currentProgress + step;
      $progressBar.css('width', newProgress + '%');
      previousProgress = newProgress;
      progressAnimationFrame = requestAnimationFrame(animateProgress);
    } else {
      // Final value
      $progressBar.css('width', targetProgress + '%');
      previousProgress = targetProgress;
    }
  };
  
  // Start animation only if there's a significant change
  if (Math.abs(targetProgress - previousProgress) > 0.1) {
    animateProgress();
  } else {
    // Small change - update directly
    $progressBar.css('width', targetProgress + '%');
    previousProgress = targetProgress;
  }
  
  // Add/remove running class for shimmer effect
  if (status === 'running') {
    $progressBar.addClass('is-running');
  } else {
    $progressBar.removeClass('is-running');
  }

  $('#mt_primelink-progress-text').text(Math.round(progress) + '%');
  $('#mt_primelink-progress-count').text(current + ' / ' + total);
  $('#mt_primelink-progress-status').text(status);
  $('#mt_primelink-run-total').text(total);
  $('#mt_primelink-run-current').text(current);
  
  // Calculate and display ETA
  const eta = calculateETA(current, total, status);
  const $etaStat = $('#mt_primelink-run-eta-stat');
  const $etaValue = $('#mt_primelink-run-eta');
  
  // Show ETA when running and we have valid data (including "Calculating...")
  if (status === 'running' && total > 0 && current < total) {
    $etaStat.css('display', 'block'); // Use block for grid item
    $etaValue.text(eta);
  } else {
    $etaStat.css('display', 'none');
    $etaValue.text('--');
  }

  $('#mt_primelink-run-status-pill')
    .text(statusText)
    .removeClass('is-running is-paused is-finished is-idle')
    .addClass('is-' + status);

  if(status === 'running'){
    $('#mt_primelink-start-import').prop('disabled', true);
    $('#mt_primelink-pause-import').prop('disabled', false);
    $('#mt_primelink-resume-import').prop('disabled', true);
  } else if(status === 'paused'){
    $('#mt_primelink-start-import').prop('disabled', true);
    $('#mt_primelink-pause-import').prop('disabled', true);
    $('#mt_primelink-resume-import').prop('disabled', false);
  } else if(status === 'finished'){
    $('#mt_primelink-start-import').prop('disabled', true);
    $('#mt_primelink-pause-import').prop('disabled', true);
    $('#mt_primelink-resume-import').prop('disabled', true);
    
    // Check if finished with anomalies
    const importId = currentImportId();
    if (importId > 0) {
      if (state.has_anomalies && state.anomalies_count > 0) {
        // Show inline button and also open modal once to draw attention.
        $('#mt_primelink-review-anomalies').show();
        setTimeout(() => {
          showAnomaliesModal(importId);
        }, 500);
      } else {
        // If state flags are missing, still try to detect anomalies via REST.
        restPost('/import/anomalies', { import_id: importId }).then(response => {
          if (response.anomalies && response.anomalies.length > 0) {
            $('#mt_primelink-review-anomalies').show();
          }
        }).catch(() => {});
      }
    }
  } else {
    $('#mt_primelink-start-import').prop('disabled', false);
    $('#mt_primelink-pause-import').prop('disabled', true);
    $('#mt_primelink-resume-import').prop('disabled', true);
  }

  const log = state.log || [];
  if(log.length){
    $('#mt_primelink-run-log').html(
      log.slice(-200).map(l => '<div class="mt_primelink-run-log__line">'+escapeHtml(String(l))+'</div>').join('')
    );
  } else {
    // Show different message based on status
    if(status === 'running'){
      $('#mt_primelink-run-log').html('<div class="mt_primelink-run-log__placeholder">Import is running... Logs will appear here.</div>');
    } else if(status === 'paused'){
      $('#mt_primelink-run-log').html('<div class="mt_primelink-run-log__placeholder">Import is paused.</div>');
  } else {
    $('#mt_primelink-run-log').html('<div class="mt_primelink-run-log__placeholder">Waiting for import to start...</div>');
    }
  }
}

let runnerPollTimer = null;
let runnerBackground = false;

async function pollRunner(){
  if(runnerBackground){ return; }
  const importId = currentImportId();

  try {
    const state = await restPost('/import/run/status', { import_id: importId });
    renderRunnerState(state);

    // Check if import finished with anomalies
    if(state.status === 'finished' && state.has_anomalies && state.anomalies_count > 0){
      // Small delay to ensure state is saved
      setTimeout(() => {
        showAnomaliesModal(importId);
      }, 500);
    }

    // Faster polling when running for smoother updates
    if(state.status === 'running'){
      runnerPollTimer = setTimeout(pollRunner, 800); // Reduced from 1500ms to 800ms for smoother updates
    } else {
      runnerPollTimer = setTimeout(pollRunner, 2000);
    }
  } catch(e){
    runnerPollTimer = setTimeout(pollRunner, 2500);
  }
}

// Reset ETA tracking when import starts
function resetETATracking() {
  etaStartTime = null;
  etaLastUpdateTime = null;
  etaLastCurrent = 0;
  etaItemsPerSecond = 0;
}

$(document).on('click', '#mt_primelink-start-import', async function(){
  runnerBackground = false;
  const importId = currentImportId();
  if(!importId || importId < 1){
    alert('Invalid import ID. Please refresh the page and try again.');
    return;
  }
  
  // Disable button immediately for better UX
  const $startBtn = $('#mt_primelink-start-import');
  $startBtn.prop('disabled', true);
  
  // Reset ETA tracking when starting new import
  resetETATracking();
  
  // Update UI optimistically
  $('#mt_primelink-run-status-pill')
    .text('Starting...')
    .removeClass('is-running is-paused is-finished is-idle')
    .addClass('is-running');
  $('#mt_primelink-progress-status').text('Starting...');
  $('#mt_primelink-run-log').html('<div class="mt_primelink-run-log__placeholder">Starting import...</div>');
  
  try {
    const state = await restPost('/import/run/start', { import_id: importId });
    renderRunnerState(state);
    
    // Start polling immediately
    pollRunner();
  } catch(e){
    // Re-enable button on error
    $startBtn.prop('disabled', false);
    alert('Failed to start import: ' + (e.json?.message || e.status || 'Unknown error'));
    debugConsole.error('Start import error:', e);
    
    // Reset UI on error
    $('#mt_primelink-run-status-pill')
      .text('Idle')
      .removeClass('is-running is-paused is-finished is-idle')
      .addClass('is-idle');
    $('#mt_primelink-progress-status').text('Idle');
  }
});

$(document).on('click', '#mt_primelink-pause-import', async function(){
  const importId = currentImportId();
  const state = await restPost('/import/run/pause', { import_id: importId });
  renderRunnerState(state);
});

$(document).on('click', '#mt_primelink-resume-import', async function(){
  const importId = currentImportId();
  const state = await restPost('/import/run/resume', { import_id: importId });
  renderRunnerState(state);
  pollRunner();
});

$(document).on('click', '#mt_primelink-start-import-bg', async function(){
  const importId = currentImportId();
  // Reset ETA tracking when starting background import
  resetETATracking();
  try{
    const state = await restPost('/import/run/start', { import_id: importId });
    renderRunnerState(state);
    runnerBackground = true;
    if(typeof runnerPollTimer !== 'undefined' && runnerPollTimer){
      clearTimeout(runnerPollTimer);
      runnerPollTimer = null;
    }
    $('#mt_primelink-run-bg-note').show();
    if($('#mt_primelink-run-bg-notify').is(':checked')){
      alert('Import started in background. You can navigate away.');
    }
  }catch(e){
    alert(e.json?.message || 'Failed to start import.');
  }
});

// -----------------------------
// Manage Imports page controls
// -----------------------------

function ensureAnomalyButton(importId, count) {
  if (!count || count <= 0) {
    // Nu ascundem butonul chiar dacă între timp anomaliile au fost rezolvate;
    // vrem ca utilizatorul să poată vedea că au existat și să verifice manual.
    return;
  }
  const $row = $('.mt_primelink-import-row[data-id="' + importId + '"]');
  if (!$row.length) return;
  const $actions = $row.find('.mt_primelink-import-actions');
  if (!$actions.length) return;

  let $btn = $actions.find('.mt-primelink-review-anomalies');
  const label = 'Review Anomalies (' + count + ')';

  if ($btn.length === 0) {
    $btn = $('<button type="button" class="button mt-primelink-review-anomalies" data-id="' + importId + '"></button>');
    $actions.prepend($btn);
  }

  $btn.text(label).show();
}

function updateManageRow(importId, state){
  const $row = $('.mt_primelink-import-row[data-id="'+importId+'"]');
  if(!$row.length) return;

  const status = state.status || 'idle';
  const progress = state.progress || 0;
  const current = state.current || 0;
  const total = state.total || 0;

  $row.find('.mt_primelink-status-pill')
    .attr('class','mt_primelink-status-pill mt_primelink-status-' + status)
    .text(status.charAt(0).toUpperCase() + status.slice(1));

  $row.find('.mt_primelink-dash-progress__bar').css('width', Math.max(0, Math.min(100, progress)) + '%');
  $row.find('.muted').text(current + ' / ' + total);

  // Toggle buttons
  $row.find('.mt_primelink-import-start').prop('disabled', status === 'running' || status === 'paused');
  $row.find('.mt_primelink-import-pause').prop('disabled', status !== 'running');
  $row.find('.mt_primelink-import-resume').prop('disabled', status !== 'paused');
}

async function refreshManageRow(importId){
  try {
    const state = await restPost('/import/run/status', { import_id: importId });
    updateManageRow(importId, state);
    
    // Always check anomalies for this import and ensure the Review button appears
    try {
      const resp = await restPost('/import/anomalies', { import_id: importId });
      const anomalies = resp.anomalies || [];
      ensureAnomalyButton(importId, anomalies.length);
    } catch(e) {
      // ignore
    }
  } catch(e){}
}

$(document).on('click', '.mt_primelink-import-pause', async function(){
  const importId = parseInt($(this).data('id'), 10);
  try{
    const state = await restPost('/import/run/pause', { import_id: importId });
    updateManageRow(importId, state);
    // Pause polling but don't stop it completely (might resume)
  } catch(e){}
});

$(document).on('click', '#mt_primelink-imports-refresh', async function(){
  $('.mt_primelink-import-row').each(function(){
    const id = parseInt($(this).data('id'), 10);
    refreshManageRow(id);
  });
});

// Real-time progress updates for imports with optimized polling
// Similar to exports - faster polling when running, slower on errors
let importPollIntervals = {};

function startImportPolling(importId) {
  // Clear existing interval if any
  if (importPollIntervals[importId]) {
    clearInterval(importPollIntervals[importId]);
  }

  let pollCount = 0;
  let currentInterval = 1500; // Start with 1.5s for running imports

  const poll = async function() {
    try {
      const state = await restPost('/import/run/status', { import_id: importId });
      updateManageRow(importId, state);
      
      // Check if finished with anomalies
      if (state.status === 'finished' && state.has_anomalies && state.anomalies_count > 0) {
        const $row = $('.mt_primelink-import-row[data-id="' + importId + '"]');
        const $actions = $row.find('.mt_primelink-import-actions');
        if ($actions.length > 0 && $actions.find('.mt-primelink-review-anomalies').length === 0) {
          const $btn = $('<button type="button" class="button mt-primelink-review-anomalies" data-id="' + importId + '">Review Anomalies (' + state.anomalies_count + ')</button>');
          $actions.prepend($btn);
        }
      }

      const status = (state.status || 'idle').toLowerCase();
      
      // Stop polling if completed, error, or idle
      if (status === 'completed' || status === 'error' || status === 'idle' || status === 'finished') {
        stopImportPolling(importId);
        return;
      }

      // Adjust interval based on status
      if (status === 'running') {
        currentInterval = 1500; // Fast polling when running
      } else if (status === 'paused') {
        currentInterval = 2500; // Slower when paused
      }

      // Restart with new interval
      stopImportPolling(importId);
      importPollIntervals[importId] = setTimeout(poll, currentInterval);
      
    } catch (e) {
      // On error, use slower polling
      currentInterval = 2500;
      stopImportPolling(importId);
      importPollIntervals[importId] = setTimeout(poll, currentInterval);
    }
  };

  // Start polling
  poll();
}

function stopImportPolling(importId) {
  if (importPollIntervals[importId]) {
    clearTimeout(importPollIntervals[importId]);
    delete importPollIntervals[importId];
  }
}

// Initialize polling for all running/paused imports on page load
$(document).ready(function() {
    $('.mt_primelink-import-row').each(function() {
    const status = $(this).find('.mt_primelink-status-pill').text().toLowerCase().trim();
      if (status === 'running' || status === 'paused') {
        const id = parseInt($(this).data('id'), 10);
      if (id > 0) {
        startImportPolling(id);
      }
    }
  });
});

// Start polling when import starts
// Reload Import - duplicate an import with same settings
$(document).on('click', '.mt_primelink-import-reload', async function(){
  const importId = $(this).data('id');
  if(!importId){
    return;
  }
  
  if(!confirm('Create a new import with the same settings as Import #' + importId + '?')){
    return;
  }
  
  try {
    const $row = $(this).closest('.mt_primelink-import-row');
    const importType = $row.data('type') || 'products';
    const sourceImportId = parseInt(importId, 10);
    const res = await restPost('/import/reload', { import_id: importId });
    if(res && res.import_id){
      // Redirect to new import wizard
      const baseAdminUrl = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) ? mtPrimeLink.adminUrl : (window.mtPrimeLinkWizard && mtPrimeLinkWizard.adminUrl ? mtPrimeLinkWizard.adminUrl : (window.ajaxurl ? window.ajaxurl.replace(/admin-ajax\.php.*/, '') : '/wp-admin/'));
      const targetUrl = baseAdminUrl
        + 'admin.php?page=mt-primelink-new-import'
        + '&import_id=' + encodeURIComponent(res.import_id)
        + '&import_type=' + encodeURIComponent(importType)
        + '&step=5'
        // Pass original import as "template" so mapping can be auto-loaded from it.
        + '&template=' + encodeURIComponent('import:' + sourceImportId);
      window.location.href = targetUrl;
    } else {
      showToastOrAlert('error', 'Failed to reload import.', 'Reload');
    }
  } catch(e){
    showToastOrAlert('error', 'Failed to reload import: ' + (e.json?.message || e.message || 'Unknown error'), 'Reload');
  }
});

$(document).on('click', '.mt_primelink-import-start', async function(){
  const importId = parseInt($(this).data('id'), 10);
  try{
    const state = await restPost('/import/run/start', { import_id: importId });
    updateManageRow(importId, state);
    startImportPolling(importId);
  } catch(e){}
});

// Update polling when import resumes
$(document).on('click', '.mt_primelink-import-resume', async function(){
  const importId = parseInt($(this).data('id'), 10);
  try{
    const state = await restPost('/import/run/resume', { import_id: importId });
    updateManageRow(importId, state);
    startImportPolling(importId);
  } catch(e){}
});

// Download log (Import)
$(document).on('click', '.mt_primelink-import-log', async function(){
  const importId = parseInt($(this).data('id'), 10);
  try{
    const res = await restPost('/import/log', { import_id: importId });
    const logArr = res.log || [];
    const text = logArr.join('\n');
    const blob = new Blob([text], {type:'text/plain'});
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'import-'+importId+'-log.txt';
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
  }catch(e){
    alert(e.json?.message || 'Failed to download log.');
  }
});

// Download log (Export)
$(document).on('click', '.mt_primelink-export-log', async function(){
  const exportId = parseInt($(this).data('id'), 10);
  try{
    const res = await restPost('/export/log', { export_id: exportId });
    const logArr = res.log || [];
    const text = logArr.join('\n');
    const blob = new Blob([text], {type:'text/plain'});
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'export-'+exportId+'-log.txt';
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
  }catch(e){
    alert(e.json?.message || 'Failed to download log.');
  }
});

// Select all toggle (per category)
$(document).on('change', '.mt_primelink-imports-select-all', function(){
  const checked = $(this).is(':checked');
  const type = $(this).data('type') || '';
  const $section = $(this).closest('.mt_primelink-import-section');
  if(type){
    $section.find('.mt_primelink-import-select[data-type="'+type+'"]').prop('checked', checked);
  } else {
    $section.find('.mt_primelink-import-select').prop('checked', checked);
  }
});

// Select all for scheduled imports
$(document).on('change', '.mt_primelink-imports-select-all-scheduled', function(){
  const checked = $(this).is(':checked');
  const type = $(this).data('type') || '';
  const $section = $(this).closest('.mt_primelink-import-section-scheduled');
  if(type){
    $section.find('.mt_primelink-import-select[data-type="'+type+'"]').prop('checked', checked);
  } else {
    $section.find('.mt_primelink-import-select').prop('checked', checked);
  }
});

// Bulk delete (per category)
$(document).on('click', '.mt_primelink-import-delete-selected', async function(e){
  e.preventDefault();
  e.stopPropagation();
  
  const type = $(this).data('type') || '';
  let selector = '.mt_primelink-import-select:checked';
  if(type){
    selector = '.mt_primelink-import-select[data-type="'+type+'"]:checked';
  }
  
  // Get checkboxes from the same section (manual or scheduled)
  const $section = $(this).closest('.mt_primelink-import-section, .mt_primelink-import-section-scheduled');
  const ids = $section.find(selector).map(function(){ 
    return parseInt($(this).val(), 10); 
  }).get().filter(id => !isNaN(id) && id > 0);
  
  if(!ids.length){
    showToastOrAlert('warning', 'Select at least one import to delete.', 'Import');
    return;
  }
  if(!confirm('Delete selected imports? This cannot be undone.')) return;

  const $button = $(this);
  const originalText = $button.text();
  $button.prop('disabled', true).text('Deleting...');

  try{
    for(const importId of ids){
      await restPost('/import/delete-draft', { import_id: importId });
      $('.mt_primelink-import-row[data-id="'+importId+'"]').fadeOut(300, function(){ $(this).remove(); });
    }
    // Uncheck select all checkboxes
    if(type){
      $('.mt_primelink-imports-select-all[data-type="'+type+'"]').prop('checked', false);
      $('.mt_primelink-imports-select-all-scheduled[data-type="'+type+'"]').prop('checked', false);
    }
    showToastOrAlert('success', 'Selected imports deleted.', 'Import');
  } catch(e){
    showToastOrAlert('error', 'Delete failed: ' + (e.json?.message || 'Unknown error'), 'Import');
  } finally {
    $button.prop('disabled', false).text(originalText);
  }
});

// Export management - Select All (Manual)
$(document).on('change', '.mt_primelink-exports-select-all', function(){
  const type = $(this).data('type') || '';
  const checked = $(this).is(':checked');
  let selector = '.mt_primelink-export-select';
  if(type){
    selector = '.mt_primelink-export-select[data-type="'+type+'"]';
  }
  // Only select checkboxes in the same section (manual exports)
  $(this).closest('.mt_primelink-import-section').find(selector).prop('checked', checked);
});

// Export management - Select All (Scheduled)
$(document).on('change', '.mt_primelink-exports-select-all-scheduled', function(){
  const type = $(this).data('type') || '';
  const checked = $(this).is(':checked');
  let selector = '.mt_primelink-export-select';
  if(type){
    selector = '.mt_primelink-export-select[data-type="'+type+'"]';
  }
  // Only select checkboxes in the same section (scheduled exports)
  $(this).closest('.mt_primelink-import-section-scheduled').find(selector).prop('checked', checked);
});

// Export management - Delete selected
$(document).on('click', '.mt_primelink-export-delete-selected', async function(e){
  e.preventDefault();
  e.stopPropagation();
  
  const type = $(this).data('type') || '';
  let selector = '.mt_primelink-export-select:checked';
  if(type){
    selector = '.mt_primelink-export-select[data-type="'+type+'"]:checked';
  }
  
  // Get checkboxes from the same section (manual or scheduled)
  const $section = $(this).closest('.mt_primelink-import-section, .mt_primelink-import-section-scheduled');
  const ids = $section.find(selector).map(function(){ 
    return parseInt($(this).val(), 10); 
  }).get().filter(id => !isNaN(id) && id > 0);
  
  if(!ids.length){
    showToastOrAlert('warning', 'Select at least one export to delete.', 'Export');
    return;
  }
  if(!confirm('Delete selected exports? This cannot be undone.')) return;

  const $button = $(this);
  const originalText = $button.text();
  $button.prop('disabled', true).text('Deleting...');

  try{
    for(const exportId of ids){
      await restPost('/export/delete-draft', { export_id: exportId });
      $('.mt_primelink-export-row[data-id="'+exportId+'"]').fadeOut(300, function(){ $(this).remove(); });
    }
    // Uncheck select all checkboxes
    if(type){
      $('.mt_primelink-exports-select-all[data-type="'+type+'"]').prop('checked', false);
      $('.mt_primelink-exports-select-all-scheduled[data-type="'+type+'"]').prop('checked', false);
    }
  } catch(e){
    alert('Delete failed: ' + (e.json?.message || 'Unknown error'));
  } finally {
    $button.prop('disabled', false).text(originalText);
  }
});

// Delete products for a specific import
// Rollback import
$(document).on('click', '.mt_primelink-import-rollback', async function(){
  const importId = parseInt($(this).data('id'), 10);
  if(!importId) return;

  try {
    // Get rollback summary first
    const summary = await restPost('/import/rollback-summary', { import_id: importId });
    
    if(!summary.can_rollback){
      alert('No items to rollback for this import.');
      return;
    }

    const s = summary.summary || {};
    const created = s.total_created || 0;
    const updated = s.total_updated || 0;
    
    let msg = 'Are you sure you want to rollback this import?\n\n';
    msg += 'This will:\n';
    if(created > 0){
      msg += `- Delete ${created} created item(s)\n`;
    }
    if(updated > 0){
      msg += `- Restore ${updated} updated item(s) to their previous state\n`;
    }
    msg += '\nThis action cannot be undone!';

    if(!confirm(msg)){
      return;
    }

    // Show options
    const deleteCreated = created > 0 ? confirm('Delete created items?') : false;
    const restoreUpdated = updated > 0 ? confirm('Restore updated items to previous state?') : true;

    if(!deleteCreated && !restoreUpdated){
      alert('No action selected. Rollback cancelled.');
      return;
    }

    const $btn = $(this);
    const originalText = $btn.text();
    $btn.prop('disabled', true).text('Rolling back...');

    const res = await restPost('/import/rollback', {
      import_id: importId,
      delete_created: deleteCreated,
      restore_updated: restoreUpdated
    });

    alert(res.message || 'Rollback completed successfully.');
    
    // Refresh the row
    refreshManageRow(importId);
    
    $btn.prop('disabled', false).text(originalText);
  } catch(e){
    alert(e.json?.message || 'Rollback failed. Please try again.');
    $(this).prop('disabled', false);
  }
});

$(document).on('click', '.mt_primelink-import-delete-products', async function(){
  const importId = parseInt($(this).data('id'), 10);
  if(!importId) return;
  if(!confirm('Delete all products (and their images) imported in this session? This cannot be undone.')) return;

  try{
    const res = await restPost('/import/delete-products', { import_id: importId });
    alert('Deleted products: ' + (res.deleted_products || 0) + ' | Images removed: ' + (res.deleted_attachments || 0));
  }catch(e){
    alert(e.json?.message || 'Delete products failed.');
  }
});

// -----------------------------
// Settings page
// -----------------------------
async function loadSettings(){
  const notice = $('#mt_primelink-settings-notice');
  try{
    const res = await restGet('/settings?ts=' + Date.now());
    $('#mt_primelink-license-key').val(res.license_key || '');
    $('#mt_primelink-enable-beta').prop('checked', !!res.enable_beta);
    $('#mt_primelink-enable-logs').prop('checked', !!res.enable_logs);
    $('#mt_primelink-default-iteration-size').val(res.default_iteration_size || 20);
    $('#mt_primelink-default-speed-opt').prop('checked', !!res.default_speed_opt);
    $('#mt_primelink-fast-import-mode').prop('checked', !!res.fast_import_mode);
    $('#mt_primelink-notify-email').val(res.notify_email || '');
    $('#mt_primelink-notify-finish').prop('checked', !!res.notify_on_finish);
    $('#mt_primelink-notify-error').prop('checked', !!res.notify_on_error);
    $('#mt_primelink-notify-template-finish').val(res.notify_tpl_finish || '');
    $('#mt_primelink-notify-template-error').val(res.notify_tpl_error || '');
    initEmailBuilder(res.notify_builder || '');
    $('#mt_primelink-cleanup-days').val(res.cleanup_days || 0);
    $('#mt_primelink-uninstall-delete-data').prop('checked', !!res.uninstall_delete_data);
    $('#mt_primelink-dropbox-token').val(res.dropbox_access_token || '');
    $('#mt_primelink-dropbox-default-path').val(res.dropbox_default_path || '');
    $('#mt_primelink-gdrive-api-key').val(res.gdrive_api_key || '');
    $('#mt_primelink-gdrive-client-id').val(res.gdrive_client_id || '');
    $('#mt_primelink-gdrive-client-secret').val(res.gdrive_client_secret || '');
    $('#mt_primelink-gdrive-default-file').val(res.gdrive_default_file || '');
    $('#mt_primelink-onedrive-client-id').val(res.onedrive_client_id || '');
    $('#mt_primelink-onedrive-client-secret').val(res.onedrive_client_secret || '');
    $('#mt_primelink-onedrive-tenant-id').val(res.onedrive_tenant_id || '');
    $('#mt_primelink-onedrive-default-file').val(res.onedrive_default_file || '');
    
    // Store OneDrive access token in global object for JavaScript use
    if(!window.mtPrimeLinkSettings) window.mtPrimeLinkSettings = {};
    window.mtPrimeLinkSettings.onedrive_access_token = res.onedrive_access_token || '';
    if(window.mtPrimeLink && window.mtPrimeLinkSettings){
      if(!window.mtPrimeLink.onedrive) window.mtPrimeLink.onedrive = {};
      window.mtPrimeLink.onedrive.accessToken = window.mtPrimeLinkSettings.onedrive_access_token;
    }
    $('#mt_primelink-cron-mode').val(res.cron_mode || 'wp');
    $('#mt_primelink-cron-token').val(res.cron_token || '');
    $('#mt_primelink-source-retention').val(typeof res.source_retention_days !== 'undefined' ? res.source_retention_days : '');
    updateCronUrlTemplate(res.cron_token || '');
    $('#mt_primelink-ftp-protocol').val(res.ftp_protocol || 'ftp');
    $('#mt_primelink-ftp-host').val(res.ftp_host || '');
    $('#mt_primelink-ftp-port').val(res.ftp_port || '');
    $('#mt_primelink-ftp-user').val(res.ftp_user || '');
    $('#mt_primelink-ftp-pass').val(res.ftp_pass || '');
    $('#mt_primelink-ftp-passive').prop('checked', !!res.ftp_passive);
    $('#mt_primelink-ftp-root').val(res.ftp_root || '');
    $('#mt_primelink-ftp-default-file').val(res.ftp_default_file || '');
    
    // Load platform connectors
    if(!window.mt_primelinkSettings) window.mt_primelinkSettings = {};
    let pc = res.platform_connectors || {};
    // Ensure we store platform_connectors as a plain object, not an array
    if (Array.isArray(pc)) {
      pc = {};
    }
    window.mt_primelinkSettings.platform_connectors = pc;
    
    // Load anomaly detection settings
    $('#mt_primelink-anomaly-detection-enabled').prop('checked', !!res.anomaly_detection_enabled);
    if (res.anomaly_detection_config) {
      const config = res.anomaly_detection_config;
      $('#mt_primelink-price-drop-threshold').val(config.price_drop_threshold || 50);
      $('#mt_primelink-price-increase-threshold').val(config.price_increase_threshold || 200);
      $('#mt_primelink-price-deviation-threshold').val(config.price_deviation_threshold || 50);
      $('#mt_primelink-max-absolute-price-change').val(config.max_absolute_price_change || 0);
      $('#mt_primelink-stock-change-threshold').val(config.stock_change_threshold || 90);
      $('#mt_primelink-max-stock-threshold').val(config.max_stock_threshold || 10000);
      $('#mt_primelink-content-change-threshold').val(config.content_change_threshold || 30);
      $('#mt_primelink-require-sku').prop('checked', !!config.require_sku);
      $('#mt_primelink-pause-on-critical').prop('checked', !!config.pause_on_critical);
      $('#mt_primelink-detect-script-injection').prop('checked', !!config.detect_script_injection);
    }
    
    // Toggle anomaly detection settings visibility
    $('#mt_primelink-anomaly-detection-enabled').off('change').on('change', function(){
      $('#mt_primelink-anomaly-detection-settings').toggle($(this).is(':checked'));
    }).trigger('change');
    
    // Load module settings
    $('#mt_primelink-module-import').prop('checked', res.module_import !== 0);
    $('#mt_primelink-module-export').prop('checked', res.module_export !== 0);
    $('#mt_primelink-module-templates').prop('checked', res.module_templates !== 0);
    $('#mt_primelink-module-statistics').prop('checked', res.module_statistics !== 0);
    $('#mt_primelink-module-system_status').prop('checked', res.module_system_status !== 0);
    $('#mt_primelink-module-live_feed').prop('checked', res.module_live_feed !== 0);
    $('#mt_primelink-module-anomaly_detection').prop('checked', res.module_anomaly_detection !== 0);
    $('#mt_primelink-module-automation').prop('checked', res.module_automation !== 0);
    
    updateLicenseStatus(res.license_status || '');
    notice.hide();
  } catch(e){
    notice.text('Failed to load settings.').show();
  }
}

function updateLicenseStatus(status){
  const pill = $('#mt_primelink-license-status');
  let label = 'Not saved';
  let cls = 'mt_primelink-status-idle';
  if(status === 'saved'){
    label = 'Saved';
    cls = 'mt_primelink-status-finished';
  }
  pill.text(label)
      .removeClass('mt_primelink-status-idle mt_primelink-status-finished')
      .addClass(cls);
}

$(document).on('click', '#mt_primelink-save-settings', async function(e){
  e.preventDefault();
  const notice = $('#mt_primelink-settings-notice');
  notice.hide();

  // mask key on send? we send full key

  // Before building payload, persist current platform connector form values into memory
  // so they are included even if user didn't click "Test Connection".
  if ($('#mt_primelink-platform-connector-select').length) {
    const connectorName = $('#mt_primelink-platform-connector-select').val();
    if (connectorName) {
      const connectorCreds = {};
      $('#mt_primelink-platform-connector-fields input').each(function () {
        const fieldKey = $(this).data('field');
        if (fieldKey) {
          connectorCreds[fieldKey] = $(this).val() || '';
        }
      });

      if (!window.mt_primelinkSettings) window.mt_primelinkSettings = {};
      if (!window.mt_primelinkSettings.platform_connectors || Array.isArray(window.mt_primelinkSettings.platform_connectors)) {
        window.mt_primelinkSettings.platform_connectors = {};
      }

      if (Object.keys(connectorCreds).length > 0) {
        window.mt_primelinkSettings.platform_connectors[connectorName] = connectorCreds;
      }
    }
  }

  const payload = {
    license_key: $('#mt_primelink-license-key').val() || '',
    enable_beta: $('#mt_primelink-enable-beta').is(':checked'),
    enable_logs: $('#mt_primelink-enable-logs').is(':checked'),
    default_iteration_size: parseInt($('#mt_primelink-default-iteration-size').val(), 10) || 20,
      default_speed_opt: $('#mt_primelink-default-speed-opt').is(':checked'),
      fast_import_mode: $('#mt_primelink-fast-import-mode').is(':checked'),
    notify_email: $('#mt_primelink-notify-email').val() || '',
    notify_on_finish: $('#mt_primelink-notify-finish').is(':checked'),
    notify_on_error: $('#mt_primelink-notify-error').is(':checked'),
    notify_tpl_finish: $('#mt_primelink-notify-template-finish').val() || '',
    notify_tpl_error: $('#mt_primelink-notify-template-error').val() || '',
    notify_builder: $('#mt_primelink-notify-builder-data').val() || '',
    cleanup_days: parseInt($('#mt_primelink-cleanup-days').val(), 10) || 0,
    uninstall_delete_data: $('#mt_primelink-uninstall-delete-data').is(':checked'),
    dropbox_access_token: $('#mt_primelink-dropbox-token').val() || '',
    dropbox_default_path: $('#mt_primelink-dropbox-default-path').val() || '',
    gdrive_api_key: $('#mt_primelink-gdrive-api-key').val() || '',
    gdrive_client_id: $('#mt_primelink-gdrive-client-id').val() || '',
    gdrive_client_secret: $('#mt_primelink-gdrive-client-secret').val() || '',
    gdrive_default_file: $('#mt_primelink-gdrive-default-file').val() || '',
    onedrive_client_id: $('#mt_primelink-onedrive-client-id').val() || '',
    onedrive_client_secret: $('#mt_primelink-onedrive-client-secret').val() || '',
    onedrive_tenant_id: $('#mt_primelink-onedrive-tenant-id').val() || '',
    onedrive_default_file: $('#mt_primelink-onedrive-default-file').val() || '',
    cron_mode: $('#mt_primelink-cron-mode').val() || 'wp',
    cron_token: $('#mt_primelink-cron-token').val() || '',
    source_retention_days: parseInt($('#mt_primelink-source-retention').val(), 10) || 0,
    ftp_protocol: $('#mt_primelink-ftp-protocol').val() || 'ftp',
    ftp_host: $('#mt_primelink-ftp-host').val() || '',
    ftp_port: parseInt($('#mt_primelink-ftp-port').val(), 10) || 0,
    ftp_user: $('#mt_primelink-ftp-user').val() || '',
    ftp_pass: $('#mt_primelink-ftp-pass').val() || '',
    // Anomaly Detection Settings
    anomaly_detection_enabled: $('#mt_primelink-anomaly-detection-enabled').is(':checked'),
    anomaly_detection_config: {
      price_drop_threshold: parseInt($('#mt_primelink-price-drop-threshold').val(), 10) || 50,
      price_increase_threshold: parseInt($('#mt_primelink-price-increase-threshold').val(), 10) || 200,
      price_deviation_threshold: parseInt($('#mt_primelink-price-deviation-threshold').val(), 10) || 50,
      max_absolute_price_change: parseFloat($('#mt_primelink-max-absolute-price-change').val()) || 0,
      stock_change_threshold: parseInt($('#mt_primelink-stock-change-threshold').val(), 10) || 90,
      max_stock_threshold: parseInt($('#mt_primelink-max-stock-threshold').val(), 10) || 10000,
      content_change_threshold: parseInt($('#mt_primelink-content-change-threshold').val(), 10) || 30,
      require_sku: $('#mt_primelink-require-sku').is(':checked'),
      pause_on_critical: $('#mt_primelink-pause-on-critical').is(':checked'),
      detect_script_injection: $('#mt_primelink-detect-script-injection').is(':checked'),
    },
    ftp_passive: $('#mt_primelink-ftp-passive').is(':checked'),
    ftp_root: $('#mt_primelink-ftp-root').val() || '',
    ftp_default_file: $('#mt_primelink-ftp-default-file').val() || '',
    platform_connectors: (function(){
      let pc = window.mt_primelinkSettings?.platform_connectors || {};
      // Ensure we don't send an array here; use plain object so JSON keeps keys
      if (Array.isArray(pc)) {
        pc = {};
      }
      return pc;
    })(),
    // Module settings
    module_import: $('#mt_primelink-module-import').is(':checked'),
    module_export: $('#mt_primelink-module-export').is(':checked'),
    module_templates: $('#mt_primelink-module-templates').is(':checked'),
    module_statistics: $('#mt_primelink-module-statistics').is(':checked'),
    module_system_status: $('#mt_primelink-module-system_status').is(':checked'),
    module_live_feed: $('#mt_primelink-module-live_feed').is(':checked'),
    module_anomaly_detection: $('#mt_primelink-module-anomaly_detection').is(':checked'),
    module_automation: $('#mt_primelink-module-automation').is(':checked')
  };

  try{
    const res = await restPost('/settings/save', payload);
    updateLicenseStatus(res.settings?.license_status || '');
    notice.text('Settings saved.').show();
  } catch(err){
    notice.text('Failed to save settings: ' + (err.json?.message || err.message || 'Unknown error')).show();
  }
});


  // Google Drive OAuth
  $(document).on('click', '#mt_primelink-gdrive-auth', async function(){
    const $btn = $(this);
    const $status = $('#mt_primelink-gdrive-auth-status');
    $btn.prop('disabled', true);
    $status.html('<em>Preparing authentication...</em>');
    
    try {
      const res = await restPost('/settings/gdrive/auth', {});
      if(res.success && res.auth_url){
        if(res.redirect_uri){
          $status.html('<div style="margin-top:8px; padding:8px; background:#fff3cd; border-left:4px solid #ffc107; border-radius:4px;"><strong>Important:</strong> Make sure this Redirect URI is added in Google Cloud Console:<br><code style="font-size:11px; word-break:break-all;">' + escapeHtml(res.redirect_uri) + '</code></div>');
          setTimeout(function(){
            window.location.href = res.auth_url;
          }, 3000);
        } else {
          window.location.href = res.auth_url;
        }
      } else {
        $status.html('<span style="color:#f00;">' + escapeHtml(res.message || 'Failed to get auth URL') + '</span>');
        $btn.prop('disabled', false);
      }
    } catch(err){
      let errorMsg = err.json?.message || err.message || 'Failed to start authentication';
      if(err.status === 401 || err.status === 403){
        errorMsg += '<br><small>Please make sure you have the required permissions (manage_woocommerce or manage_options).</small>';
      }
      $status.html('<span style="color:#f00;">' + escapeHtml(errorMsg) + '</span>');
      $btn.prop('disabled', false);
    }
  });

  $(document).on('click', '#mt_primelink-gdrive-revoke', async function(){
    if(!confirm('Are you sure you want to revoke Google Drive access?')){
      return;
    }
    
    const $btn = $(this);
    $btn.prop('disabled', true).text('Revoking...');
    
    try {
      const res = await restPost('/settings/gdrive/revoke', {});
      if(res.success){
        location.reload();
      } else {
        alert('Failed to revoke access: ' + (res.message || 'Unknown error'));
        $btn.prop('disabled', false).text('Revoke Access');
      }
    } catch(err){
      alert('Failed to revoke access: ' + (err.json?.message || err.message || 'Unknown error'));
      $btn.prop('disabled', false).text('Revoke Access');
    }
  });

  // Show success/error message if redirected from OAuth
  $(document).ready(function(){
    const urlParams = new URLSearchParams(window.location.search);
    const $status = $('#mt_primelink-gdrive-auth-status');
    
    if(urlParams.get('gdrive_auth') === 'success'){
      $status.html('<span style="color:#0a0;">✓ Authentication successful! Reloading settings...</span>');
      // Reload settings to get the new access token
      loadSettings().then(function() {
        // Update mtPrimeLink object with new access token
        if (window.mtPrimeLink && window.mtPrimeLinkSettings) {
          const newAccessToken = window.mtPrimeLinkSettings.gdrive_access_token || '';
          if (window.mtPrimeLink.gdrive) {
            window.mtPrimeLink.gdrive.accessToken = newAccessToken;
          }
        }
      });
    } else if(urlParams.get('gdrive_auth') === 'error'){
      const error = urlParams.get('error') || 'Unknown error';
      const errorDesc = urlParams.get('error_desc') || '';
      let errorMsg = 'Authentication failed: ' + error;
      if(errorDesc) errorMsg += ' - ' + errorDesc;
      $status.html('<span style="color:#d00;">✗ ' + escapeHtml(errorMsg) + '</span>');
    }
  });

  // OneDrive OAuth handlers
  $(document).on('click', '#mt_primelink-onedrive-auth', async function(){
    const $btn = $(this);
    const $status = $('#mt_primelink-onedrive-auth-status');
    $btn.prop('disabled', true);
    $status.html('<em>Preparing connection...</em>');
    
    try {
      // First, save the settings (Client ID, Client Secret, Tenant ID) before authentication
      const clientId = $('#mt_primelink-onedrive-client-id').val() || '';
      const clientSecret = $('#mt_primelink-onedrive-client-secret').val() || '';
      const tenantId = $('#mt_primelink-onedrive-tenant-id').val() || '';
      
      if (!clientId || !clientSecret) {
        $status.html('<span style="color:#d00;">✗ ' + escapeHtml('Client ID and Client Secret are required. Please fill them in and try again.') + '</span>');
        $btn.prop('disabled', false);
        return;
      }
      
      // Save settings first (automatically before authentication)
      $status.html('<em>Saving settings...</em>');
      const savePayload = {
        onedrive_client_id: clientId,
        onedrive_client_secret: clientSecret,
        onedrive_tenant_id: tenantId,
      };
      try {
        const saveRes = await restPost('/settings/save', savePayload);
        if (!saveRes || !saveRes.saved) {
          throw new Error('Failed to save settings');
        }
      } catch (saveErr) {
        $status.html('<span style="color:#d00;">✗ ' + escapeHtml('Failed to save settings: ' + (saveErr.json?.message || saveErr.message || 'Unknown error')) + '</span>');
        $btn.prop('disabled', false);
        return;
      }
      
      // Now proceed with authentication
      $status.html('<em>Preparing connection...</em>');
      const res = await restPost('/settings/onedrive/auth', { use_pkce: false });
      if(res.success && res.auth_url){
        if(res.redirect_uri){
          $status.html('<div style="margin-top:8px; padding:8px; background:#fff3cd; border-left:4px solid #ffc107; border-radius:4px;"><strong>Important:</strong> Make sure this Redirect URI is added in Azure AD App Registration → Authentication:<br><code style="font-size:11px; word-break:break-all;">' + escapeHtml(res.redirect_uri) + '</code></div>');
          setTimeout(function(){
            window.location.href = res.auth_url;
          }, 2000);
        } else {
          window.location.href = res.auth_url;
        }
      } else {
        const errorMsg = res.message || res.error || 'Failed to get authentication URL';
        $status.html('<span style="color:#d00;">✗ ' + escapeHtml(errorMsg) + '</span>');
        if(res.error_details) {
          $status.append('<br><small style="color:#666;">' + escapeHtml(res.error_details) + '</small>');
        }
        $btn.prop('disabled', false);
      }
    } catch(err){
      const errorMsg = err.json?.error || err.json?.message || err.message || 'Unknown error';
      const errorDetails = err.json?.error_description || err.json?.data?.error_description || '';
      $status.html('<span style="color:#d00;">✗ Error: ' + escapeHtml(errorMsg) + '</span>');
      if(errorDetails) {
        $status.append('<br><small style="color:#666;">Details: ' + escapeHtml(errorDetails) + '</small>');
      }
      if(err.json) {
        debugConsole.error('OneDrive OAuth Error:', err.json);
      }
      $btn.prop('disabled', false);
    }
  });

  $(document).on('click', '#mt_primelink-onedrive-revoke', async function(){
    if(!confirm('Are you sure you want to revoke OneDrive access?')){
      return;
    }
    
    const $btn = $(this);
    $btn.prop('disabled', true).text('Revoking...');
    
    try {
      const res = await restPost('/settings/onedrive/revoke', {});
      if(res.success){
        alert('OneDrive access revoked successfully.');
        location.reload();
      } else {
        alert('Failed to revoke access: ' + (res.message || 'Unknown error'));
        $btn.prop('disabled', false).text('Revoke Access');
      }
    } catch(err){
      alert('Error: ' + (err.message || 'Unknown error'));
      $btn.prop('disabled', false).text('Revoke Access');
    }
  });

  // Handle OneDrive OAuth callback status
  $(document).ready(function(){
    const urlParams = new URLSearchParams(window.location.search);
    const $status = $('#mt_primelink-onedrive-auth-status');
    
    if(urlParams.get('onedrive_auth') === 'success'){
      $status.html('<span style="color:#0a0;">✓ Authentication successful! Reloading settings...</span>');
      // Reload settings to get the new access token
      loadSettings().then(function() {
        // Update mtPrimeLink object with new access token
        if (window.mtPrimeLink && window.mtPrimeLinkSettings) {
          const newAccessToken = window.mtPrimeLinkSettings.onedrive_access_token || '';
          if (window.mtPrimeLink.onedrive) {
            window.mtPrimeLink.onedrive.accessToken = newAccessToken;
          } else {
            window.mtPrimeLink.onedrive = { accessToken: newAccessToken };
          }
          // Debug logging removed for production
        }
        $status.html('<span style="color:#0a0;">✓ Authentication successful! You can now browse OneDrive files.</span>');
        setTimeout(function(){
          $status.fadeOut();
        }, 5000);
      }).catch(function() {
        $status.html('<span style="color:#0a0;">✓ Authentication successful! Please refresh the page to use OneDrive features.</span>');
      });
    } else if(urlParams.get('onedrive_auth') === 'error'){
      const error = urlParams.get('error') || 'unknown';
      const errorDesc = urlParams.get('error_desc') || '';
      let errorMsg = '<span style="color:#f00;"><strong>OAuth Error:</strong> ';
      
      if(error === 'access_denied'){
        errorMsg += 'Access denied. This usually means:<br>';
        errorMsg += '1. You denied access during the OAuth flow<br>';
        errorMsg += '2. Or your Azure AD app permissions are not configured correctly<br><br>';
        errorMsg += '<strong>Solution:</strong> In Azure Portal → App registrations → Your app → API permissions, ensure "Files.ReadWrite" and "offline_access" are granted.';
      } else if(error === 'invalid_client'){
        errorMsg += 'Invalid Client ID, Client Secret, or Tenant ID. Please check your Azure Portal settings.';
      } else if(error === 'not_logged_in'){
        errorMsg += 'You must be logged in to complete OAuth authentication. Please log in and try again.';
      } else if(error === 'invalid_state'){
        errorMsg += 'Invalid state parameter. Please try again.';
      } else if(error === 'missing_params'){
        errorMsg += 'Missing authorization parameters. Please try again.';
      } else {
        errorMsg += errorDesc || error || 'Unknown error occurred.';
      }
      
      errorMsg += '</span>';
      $status.html(errorMsg);
    }
  });

  function updateCronUrlTemplate(token){
    const base = window.location.origin;
    const url = base + '/wp-json/primelink/v1/cron/tick?token=' + encodeURIComponent(token || '{TOKEN}');
    $('#mt_primelink-cron-url-template').text(url);
  }
  $(document).on('input change', '#mt_primelink-cron-token', function(){
    updateCronUrlTemplate($(this).val());
  });

  // Test email
  $(document).on('click', '#mt_primelink-test-email', async function(){
    const email = $('#mt_primelink-notify-email').val() || '';
    const status = $('#mt_primelink-test-email-status');
    status.hide();
    if(!email){
      alert('Set a notify email first.');
      return;
    }
    $(this).prop('disabled', true);
    status.text('Sending...').show();
    try{
      const res = await restPost('/settings/notify-test', { email });
      status.text(res.message || 'Sent.').show();
    }catch(e){
      status.text(e.json?.message || 'Failed to send test email.').show();
    } finally {
      $(this).prop('disabled', false);
    }
  });

  // Email builder logic
  let mt_primelinkEmailBlocks = [];
  function syncBuilderHidden(){
    $('#mt_primelink-notify-builder-data').val(JSON.stringify(mt_primelinkEmailBlocks));
  }
  function renderEmailBlocks(){
    const wrap = $('#mt_primelink-email-blocks');
    wrap.empty();
    mt_primelinkEmailBlocks.forEach((b, idx)=>{
      let html = '<div class="mt_primelink-email-block" data-idx="'+idx+'">';
      html += '<div class="mt_primelink-email-block__header"><strong>'+b.type+'</strong>';
      html += '<div class="mt_primelink-email-block__controls">';
      html += '<button type="button" class="button mt_primelink-block-up" '+(idx===0?'disabled':'')+'>&uarr;</button>';
      html += '<button type="button" class="button mt_primelink-block-down" '+(idx===mt_primelinkEmailBlocks.length-1?'disabled':'')+'>&darr;</button>';
      html += '<button type="button" class="button mt_primelink-block-del">&times;</button>';
      html += '</div></div>';
      if(b.type === 'heading' || b.type === 'paragraph'){
        html += '<textarea class="mt_primelink-block-text" rows="'+(b.type==='heading'?2:3)+'">'+escapeHtml(b.text||'')+'</textarea>';
      }else if(b.type === 'html'){
        html += '<textarea class="mt_primelink-block-html" rows="5" placeholder="<p>Custom HTML</p>">'+escapeHtml(b.html||b.text||'')+'</textarea>';
      }else if(b.type === 'button'){
        html += '<input type="text" class="mt_primelink-block-text" placeholder="Button label" value="'+escapeHtml(b.text||'')+'"/>';
        html += '<input type="text" class="mt_primelink-block-url" placeholder="https://example.com" value="'+escapeHtml(b.url||'')+'"/>';
      }else if(b.type === 'stats'){
        html += '<div class="muted">Stats block will show Created/Updated/Skipped/Errors.</div>';
      }
      html += '</div>';
      wrap.append(html);
    });
    syncBuilderHidden();
  }
  function initEmailBuilder(raw){
    try{
      mt_primelinkEmailBlocks = raw ? JSON.parse(raw) : [];
      if(!Array.isArray(mt_primelinkEmailBlocks)) mt_primelinkEmailBlocks=[];
    }catch(e){ mt_primelinkEmailBlocks=[]; }
    if(!mt_primelinkEmailBlocks.length){
      mt_primelinkEmailBlocks = [
        {type:'heading', text:'Import #{import_id} finished'},
        {type:'stats'},
        {type:'paragraph', text:'Status: {status}'}
      ];
    }
    renderEmailBlocks();
  }
  $(document).on('click', '#mt_primelink-email-block-add', function(){
    const t = $('#mt_primelink-email-block-type').val();
    const block = {type:t};
    if(t==='heading') block.text='Import #{import_id} finished';
    if(t==='paragraph') block.text='Status: {status}';
    if(t==='html') block.html='<p><strong>Custom HTML</strong> with placeholders like {status}</p>';
    if(t==='button'){ block.text='View import'; block.url=window.location.origin; }
    mt_primelinkEmailBlocks.push(block);
    renderEmailBlocks();
  });
  $(document).on('click', '.mt_primelink-block-del', function(){
    const idx = parseInt($(this).closest('.mt_primelink-email-block').data('idx'),10);
    mt_primelinkEmailBlocks.splice(idx,1);
    renderEmailBlocks();
  });
  $(document).on('click', '.mt_primelink-block-up', function(){
    const idx = parseInt($(this).closest('.mt_primelink-email-block').data('idx'),10);
    if(idx>0){
      [mt_primelinkEmailBlocks[idx-1], mt_primelinkEmailBlocks[idx]] = [mt_primelinkEmailBlocks[idx], mt_primelinkEmailBlocks[idx-1]];
      renderEmailBlocks();
    }
  });
  $(document).on('click', '.mt_primelink-block-down', function(){
    const idx = parseInt($(this).closest('.mt_primelink-email-block').data('idx'),10);
    if(idx<mt_primelinkEmailBlocks.length-1){
      [mt_primelinkEmailBlocks[idx+1], mt_primelinkEmailBlocks[idx]] = [mt_primelinkEmailBlocks[idx], mt_primelinkEmailBlocks[idx+1]];
      renderEmailBlocks();
    }
  });
  $(document).on('input', '.mt_primelink-block-text', function(){
    const idx = parseInt($(this).closest('.mt_primelink-email-block').data('idx'),10);
    mt_primelinkEmailBlocks[idx].text = $(this).val();
    syncBuilderHidden();
  });
  $(document).on('input', '.mt_primelink-block-html', function(){
    const idx = parseInt($(this).closest('.mt_primelink-email-block').data('idx'),10);
    mt_primelinkEmailBlocks[idx].html = $(this).val();
    syncBuilderHidden();
  });
  $(document).on('input', '.mt_primelink-block-url', function(){
    const idx = parseInt($(this).closest('.mt_primelink-email-block').data('idx'),10);
    mt_primelinkEmailBlocks[idx].url = $(this).val();
    syncBuilderHidden();
  });
  $(document).on('click', '#mt_primelink-email-preview-btn', function(){
    const sample = {
      import_id: 123,
      status: 'finished',
      created: 10,
      updated: 5,
      skipped: 2,
      errors: 0
    };
    let html = '';
    mt_primelinkEmailBlocks.forEach(b=>{
      if(b.type==='heading'){
        html += '<h3>'+escapeHtml(applyPlaceholders(b.text||'', sample))+'</h3>';
      } else if(b.type==='paragraph'){
        html += '<p>'+escapeHtml(applyPlaceholders(b.text||'', sample))+'</p>';
      } else if(b.type==='html'){
        html += applyPlaceholders(b.html||b.text||'', sample);
      } else if(b.type==='stats'){
        html += '<ul>';
        html += '<li>Created: '+sample.created+'</li>';
        html += '<li>Updated: '+sample.updated+'</li>';
        html += '<li>Skipped: '+sample.skipped+'</li>';
        html += '<li>Errors: '+sample.errors+'</li>';
        html += '</ul>';
      } else if(b.type==='button'){
        const url = applyPlaceholders(b.url||'', sample);
        const txt = applyPlaceholders(b.text||'Open', sample);
        html += '<p><a href="'+escapeHtml(url)+'" style="display:inline-block;padding:8px 12px;background:#007cba;color:#fff;border-radius:6px;text-decoration:none;">'+escapeHtml(txt)+'</a></p>';
      }
    });
    $('#mt_primelink-email-preview').html(html || '<em>No blocks.</em>').show();
  });
  function applyPlaceholders(text, data){
    return text.replace(/{(\w+)}/g, (_,k)=> data[k]!==undefined ? data[k] : '');
  }

$(document).ready(function(){
  if($('.mt_primelink-settings').length){
    loadSettings();
  }
});

// Category tabs (WooCommerce / WooCommerce)
$(document).on('click', '.mt_primelink-category-tab-btn', function(e){
  e.preventDefault();
  const category = $(this).data('category');
  $('.mt_primelink-category-tab-btn').removeClass('is-active');
  $(this).addClass('is-active');
  
  // Show/hide category content
  $('.mt_primelink-category-content').removeClass('is-active');
  $('.mt_primelink-category-content[data-category-content="'+category+'"]').addClass('is-active');
  
  // Show/hide tab panels for this category
  $('.mt_primelink-tab-panel').removeClass('is-active');
  const $firstTab = $('.mt_primelink-tab-panel[data-category="'+category+'"]').first();
  if($firstTab.length){
    $firstTab.addClass('is-active');
  }
  
  // Update sub-tabs active state
  $('.mt_primelink-tab-btn[data-category="'+category+'"]').first().addClass('is-active');
  $('.mt_primelink-tab-btn[data-category="'+category+'"]').not(':first').removeClass('is-active');
});

// Settings tabs (sub-tabs within categories)
$(document).on('click', '.mt_primelink-tab-btn', function(e){
  e.preventDefault();
  const tab = $(this).data('tab');
  const category = $(this).data('category');
  
  // If category exists, only process if this tab belongs to the active category
  if(category){
    const $activeCategory = $('.mt_primelink-category-tab-btn.is-active');
    const activeCategory = $activeCategory.data('category');
    
    if(category !== activeCategory){
      return; // Ignore clicks on inactive category tabs
    }
    
    // Update tab buttons for this category
    $('.mt_primelink-tab-btn[data-category="'+category+'"]').removeClass('is-active');
    $(this).addClass('is-active');
    
    // Update tab panels for this category (only panels in the active category)
    $('.mt_primelink-tab-panel[data-category="'+category+'"]').removeClass('is-active');
    $('.mt_primelink-tab-panel[data-tab-panel="'+tab+'"][data-category="'+category+'"]').addClass('is-active');
  } else {
    // No category - handle as regular settings tabs (like in settings page)
  $('.mt_primelink-tab-btn').removeClass('is-active');
  $(this).addClass('is-active');
    
    // Update all tab panels (without category filter)
  $('.mt_primelink-tab-panel').removeClass('is-active');
  $('.mt_primelink-tab-panel[data-tab-panel="'+tab+'"]').addClass('is-active');
  }
});

function initWizardUI(){
  const $typeSelect = $('#mt_primelink-product-type-select');
  if($typeSelect.length){
    mt_primelinkSelectedProductType = $typeSelect.val() || 'simple';
  }
  const $existingPath = $('#mt-primelink-existing-path');
  if($existingPath.length){
    try{
        populateExistingRecent();
        fetchExistingUploads();
      }catch(e){}
  }
  if(getStep() !== 5){
    setSaveStatus('', '');
  }
    filterFileFields();
    bindDnD();
    markUnmappedSlots();
    toggleVariationPanel();
    updatePreviewPaginationUI();
    loadPreview();
    loadMappingPreview(window.mt_primelinkCurrentMapIndex || 0);
    fetchFilterPresets();
    // Only refresh import templates if we're on import page, not export
    if(!$('body').hasClass('mt_primelink-export-wizard') && !$('.mt_primelink-export-step-2').length){
    refreshTemplateList();
    }
    if($('.mt_primelink-import-step-7').length){
      buildSummary();
    }
    if($('.mt_primelink-import-step-8').length){
      if(typeof runnerPollTimer !== 'undefined' && runnerPollTimer){
        clearTimeout(runnerPollTimer);
        runnerPollTimer = null;
      }
      pollRunner();
    }
  }
  
  // Refresh export templates when step 2 is visible
  // NOTE: This is handled in $(function()) block below, don't duplicate
  // if($('.mt_primelink-export-step-2').length){
  //   refreshExportTemplateList();
  // }

  function collectAliasState(){
  const list = [];
  jQuery('.mt_primelink-alias-row').each(function(){
    const target = (jQuery(this).find('.mt_primelink-alias-target').val() || '').trim();
    const aliases = (jQuery(this).find('.mt_primelink-alias-values').val() || '').split(',').map(v=>v.trim()).filter(Boolean);
    if(target && aliases.length){
      list.push({ target, aliases });
    }
  });
  return list;
}

function collectOtherOptions(){
  return {
    global_prefix: jQuery('#mt_primelink-opt-global-prefix').val() || '',
    global_suffix: jQuery('#mt_primelink-opt-global-suffix').val() || '',
    global_trim: jQuery('#mt_primelink-opt-global-trim').is(':checked'),
    slugify: jQuery('#mt_primelink-opt-slugify').is(':checked'),
    fallback_image: jQuery('#mt_primelink-opt-fallback-image').val() || '',
    skip_if_empty: jQuery('#mt_primelink-opt-skip-field').val() || ''
  };
}

  // -----------------------------
  // Platform Connectors
  // -----------------------------
  let platformConnectors = {};
  let platformCredentials = {};

  async function initPlatformConnector(){
    try {
      const res = await restGet('/platform/connectors');
      platformConnectors = {};
      (res.connectors || []).forEach(c => {
        if(c && c.name){
          // Fields can be an object (associative array from PHP) or an array
          let fieldsData = c.fields || {};
          let fieldsArray = [];
          
          if(Array.isArray(fieldsData)){
            // If it's already an array, use it directly
            fieldsArray = fieldsData;
          } else if(typeof fieldsData === 'object' && fieldsData !== null){
            // Convert object to array: {key: {label, type, ...}} -> [{key, label, type, ...}]
            const keys = Object.keys(fieldsData);
            fieldsArray = keys.map(key => {
              const fieldData = fieldsData[key];
              
              // Check if fieldData is an object (not a string or other type)
              if(typeof fieldData === 'object' && fieldData !== null && !Array.isArray(fieldData)){
                // Spread all properties from fieldData and add the key
                return {
                  key: key,
                  ...fieldData
                };
              } else {
                // If fieldData is not an object, create a basic field structure
                return {
                  key: key,
                  label: key,
                  type: 'text',
                  placeholder: '',
                  required: false,
                  description: ''
                };
              }
            });
          }
          
          platformConnectors[c.name] = {
            name: c.name,
            display_name: c.display_name || c.name,
            fields: fieldsArray
          };
        }
      });
    } catch(err){
      debugConsole.error('Failed to load connectors:', err);
      platformConnectors = {};
    }
  }

  // Helper function to get current import type
  function getCurrentImportType(){
    const url = new URL(window.location.href);
    let importType = url.searchParams.get('import_type') || '';
    if(!importType && document.body.classList.contains('mt_primelink-import-orders')){
      importType = 'orders';
    } else if(!importType && document.body.classList.contains('mt_primelink-import-customers')){
      importType = 'customers';
    } else if(!importType && document.body.classList.contains('mt_primelink-import-coupons')){
      importType = 'coupons';
    } else if(!importType){
      importType = 'products'; // default
    }
    return importType;
  }

  $(document).on('change', '#mt_primelink-platform-connector', async function(){
    const connectorName = $(this).val();
    const $credentials = $('#mt_primelink-platform-credentials');
    const $fields = $('#mt_primelink-platform-credentials-fields');
    const $options = $('#mt_primelink-platform-options');
    const $dataTypeRow = $('#mt_primelink-platform-data-type-row');
    const $productsInfo = $('#mt_primelink-platform-products-info');
    
    if(!connectorName){
      $credentials.hide();
      $options.hide();
      return;
    }

    // Ensure connectors are loaded
    if(Object.keys(platformConnectors).length === 0){
      await initPlatformConnector();
    }

    const connector = platformConnectors[connectorName];
    if(!connector){
      $credentials.hide();
      $options.hide();
      return;
    }

    // Data type is automatically determined from current import type, no need for dropdown
    // Hide data type row as it's no longer needed
    $dataTypeRow.hide();

    // Fields should already be processed as array in initPlatformConnector
    // Use it directly - no need to reprocess
    let fieldsArray = Array.isArray(connector.fields) ? connector.fields : [];
    
    if(fieldsArray.length === 0){
      // Try to reload connectors
      await initPlatformConnector();
      const updatedConnector = platformConnectors[connectorName];
      if(updatedConnector && Array.isArray(updatedConnector.fields) && updatedConnector.fields.length > 0){
        fieldsArray = updatedConnector.fields;
      } else {
        $credentials.hide();
        $options.hide();
        return;
      }
    }

    // Load saved credentials from settings
    const savedCreds = window.mt_primelinkSettings?.platform_connectors?.[connectorName] || {};
    
    // Render credentials fields
    let html = '';
    fieldsArray.forEach((field) => {
      // Use the key from the field object (e.g., 'shop_domain', 'api_key')
      const fieldKey = field.key || '';
      const fieldId = 'mt_primelink-platform-' + fieldKey.replace(/[^a-z0-9-]/gi, '-');
      
      // Use label from field, fallback to key if label doesn't exist
      const fieldLabel = field.label || fieldKey;
      
      // Get saved value if exists
      const savedValue = savedCreds[fieldKey] || '';
      
      html += '<div class="mt_primelink-form-row">';
      html += '<label for="' + fieldId + '">' + escapeHtml(fieldLabel);
      if(field.required) html += ' <span style="color:#f00;">*</span>';
      html += '</label>';
      
      const isPassword = field.type === 'password';
      
      // For BigCommerce access_token, ensure no maxlength restriction
      let inputAttrs = '';
      if(fieldKey === 'access_token' && connectorName === 'bigcommerce'){
        // BigCommerce tokens can be 64+ characters, ensure no maxlength
        inputAttrs = 'maxlength=""';
      } else if(field.maxlength){
        inputAttrs = 'maxlength="' + escapeHtml(field.maxlength) + '"';
      }
      
      if(isPassword){
        html += '<div style="position:relative;">';
        html += '<input type="password" id="' + fieldId + '" class="widefat" placeholder="' + escapeHtml(field.placeholder || '') + '" data-field="' + escapeHtml(fieldKey) + '" value="' + escapeHtml(savedValue) + '" style="padding-right:80px;" ' + inputAttrs + '>';
        html += '<button type="button" class="button" data-toggle-password="' + fieldId + '" style="position:absolute;right:4px;top:50%;transform:translateY(-50%);height:calc(100% - 8px);padding:0 12px;font-size:12px;line-height:1;">Show</button>';
        html += '</div>';
      } else {
        html += '<input type="text" id="' + fieldId + '" class="widefat" placeholder="' + escapeHtml(field.placeholder || '') + '" data-field="' + escapeHtml(fieldKey) + '" value="' + escapeHtml(savedValue) + '" ' + inputAttrs + '>';
      }
      
      if(field.description){
        html += '<p class="description">' + escapeHtml(field.description) + '</p>';
      }
      html += '</div>';
    });
    
    $fields.html(html);
    $credentials.show();
    $options.hide();
    
    // If we have saved credentials, auto-populate platformCredentials
    // User can still modify them if needed, and test connection again
    if(Object.keys(savedCreds).length > 0){
      platformCredentials[connectorName] = savedCreds;
      // Show a message that credentials were loaded from settings
      const $status = $('#mt_primelink-platform-connection-status');
      if($status.length){
        $status.html('<span style="color:#0a0;font-size:12px;">&#10003; Credentials loaded from settings. You can modify them or test connection.</span>');
      }
    }
  });

  $(document).on('click', '#mt_primelink-platform-test-connection', async function(){
    const connectorName = $('#mt_primelink-platform-connector').val();
    if(!connectorName){
      alert('Please select a platform first.');
      return;
    }

    const connector = platformConnectors[connectorName];
    if(!connector){
      alert('Connector not found.');
      return;
    }

    // Collect credentials
    const credentials = {};
    $('#mt_primelink-platform-credentials-fields input').each(function(){
      const $input = $(this);
      const fieldName = $input.data('field');
      if(fieldName){
        credentials[fieldName] = $input.val() || '';
      }
    });

    // Validate required fields
    let missing = [];
    const fieldsObj = connector.fields || {};
    let fieldsToCheck = [];
    
    if(Array.isArray(fieldsObj)){
      fieldsToCheck = fieldsObj;
    } else {
      fieldsToCheck = Object.keys(fieldsObj).map(key => {
        return { key: key, ...fieldsObj[key] };
      });
    }
    
    fieldsToCheck.forEach(field => {
      if(field.required){
        const fieldName = field.key || field.label.toLowerCase().replace(/\s+/g, '_');
        if(!credentials[fieldName] || credentials[fieldName].trim() === ''){
          missing.push(field.label || fieldName);
        }
      }
    });

    if(missing.length > 0){
      alert('Please fill in required fields: ' + missing.join(', '));
      return;
    }

    const $btn = $(this);
    const $status = $('#mt_primelink-platform-connection-status');
    $btn.prop('disabled', true).text('Testing...');
    $status.html('');

    try {
      const res = await restPost('/platform/test-connection', {
        connector: connectorName,
        credentials: credentials
      });

      if(res.success){
        $status.html('<span style="color:#0a0;">&#10003; Connection successful! Fetching data...</span>');
        platformCredentials[connectorName] = credentials;
        $('#mt_primelink-platform-options').show();
        
        // Automatically fetch and preview data after successful connection
        const importType = getCurrentImportType();
        // Normalize import type to data_type format
        let dataType = importType;
        if(dataType === 'product'){
          dataType = 'products';
        } else if(dataType === 'order'){
          dataType = 'orders';
        } else if(dataType === 'customer'){
          dataType = 'customers';
        } else if(dataType === 'coupon'){
          dataType = 'coupons';
        } else if(dataType === 'category'){
          dataType = 'categories';
        } else if(dataType === 'tag'){
          dataType = 'tags';
        } else if(dataType === 'attribute'){
          dataType = 'attributes';
        } else if(dataType === 'variation'){
          dataType = 'variations';
        } else if(dataType === 'review'){
          dataType = 'reviews';
        } else if(dataType === 'download'){
          dataType = 'downloads';
        }
        
        // Automatically trigger fetch and preview
        await fetchAndPreviewPlatformData(connectorName, credentials, dataType);
      } else {
        $status.html('<span style="color:#f00;">&#10007; ' + escapeHtml(res.message || 'Connection failed') + '</span>');
      }
    } catch(err){
      $status.html('<span style="color:#f00;">&#10007; ' + escapeHtml(err.json?.message || 'Connection failed') + '</span>');
    } finally {
      $btn.prop('disabled', false).text('Test Connection');
    }
  });

  $(document).on('change', '#mt_primelink-platform-realtime', function(){
    const enabled = $(this).is(':checked');
    $('#mt_primelink-platform-realtime-options').toggle(enabled);
  });

  // Function to fetch and preview platform data (can be called automatically or manually)
  async function fetchAndPreviewPlatformData(connectorName, credentials, dataType){
    if(!connectorName || !credentials || !dataType){
      debugConsole.error('fetchAndPreviewPlatformData: Missing parameters', {
        connectorName: !!connectorName,
        credentials: !!credentials,
        dataType: !!dataType
      });
      return;
    }

    // Validate credentials before sending
    if(!credentials.access_token || credentials.access_token.trim() === ''){
      debugConsole.error('fetchAndPreviewPlatformData: Missing or empty access_token', {
        has_access_token: !!credentials.access_token,
        credentials_keys: Object.keys(credentials)
      });
      alert('Error: Access token is missing or empty. Please test the connection again.');
      return;
    }

    // Token length for BigCommerce can vary; we only validate that it is non-empty.
    // Detailed permission/auth issues are handled by server-side error messages.

    const $preview = $('#mt_primelink-platform-preview');
    const $content = $('#mt_primelink-platform-preview-content');
    const $status = $('#mt_primelink-platform-connection-status');
    
    $preview.hide();
    $content.html('');
    if($status.length){
      $status.html('<span style="color:#0a0;">&#10003; Connection successful! Fetching data...</span>');
    }

    try {
      const res = await restPost('/platform/fetch', {
        connector: connectorName,
        credentials: credentials,
        data_type: dataType,
        limit: 5,
        page: 1
      });

      if(res.success){
        let html = '<p><strong>Total items:</strong> ' + (res.total || 0) + '</p>';
        
        if(res.preview){
          html += '<div style="margin-top:12px;padding:12px;background:rgba(0,0,0,0.3);border-radius:4px;max-height:300px;overflow-y:auto;">';
          html += '<h5>Preview (first item converted):</h5>';
          html += '<pre style="white-space:pre-wrap;font-size:12px;">' + escapeHtml(JSON.stringify(res.preview, null, 2)) + '</pre>';
          html += '</div>';
        }

        if(res.data && res.data.length > 0){
          html += '<div style="margin-top:12px;"><strong>Raw data (first ' + res.data.length + ' items):</strong></div>';
          res.data.slice(0, 3).forEach((item, idx) => {
            html += '<div style="margin-top:8px;padding:8px;background:rgba(0,0,0,0.2);border-radius:4px;">';
            html += '<strong>Item ' + (idx + 1) + ':</strong><br>';
            html += '<pre style="white-space:pre-wrap;font-size:11px;margin-top:4px;">' + escapeHtml(JSON.stringify(item, null, 2).substring(0, 500)) + '...</pre>';
            html += '</div>';
          });
        }

        $content.html(html);
        $preview.show();
        
        if($status.length){
          $status.html('<span style="color:#0a0;">&#10003; Connection successful! Data fetched.</span>');
        }

        // Save to draft
        const importId = currentImportId();
        if(importId){
          try {
            await restPost('/import/save-step', {
              import_id: importId,
              step: 2,
              data: {
                source: 'platform',
                connector: connectorName,
                credentials: credentials,
                data_type: dataType,
                realtime_enabled: $('#mt_primelink-platform-realtime').is(':checked'),
                realtime_events: $('.mt_primelink-platform-event:checked').map(function(){ return $(this).val(); }).get()
              }
            });
          } catch(e){
            debugConsole.error('Failed to save platform config:', e);
          }
        }
      } else {
        $content.html('<p style="color:#f00;">' + escapeHtml(res.error || 'Failed to fetch data') + '</p>');
        $preview.show();
        if($status.length){
          $status.html('<span style="color:#f00;">&#10007; Failed to fetch data: ' + escapeHtml(res.error || 'Unknown error') + '</span>');
        }
      }
    } catch(err){
      $content.html('<p style="color:#f00;">' + escapeHtml(err.json?.message || 'Failed to fetch data') + '</p>');
      $preview.show();
      if($status.length){
        $status.html('<span style="color:#f00;">&#10007; Failed to fetch data: ' + escapeHtml(err.json?.message || 'Unknown error') + '</span>');
      }
    }
  }

  // Keep the button handler for manual trigger if needed (though button is now hidden)
  $(document).on('click', '#mt_primelink-platform-fetch-preview', async function(){
    const connectorName = $('#mt_primelink-platform-connector').val();
    const importType = getCurrentImportType();
    let dataType = importType;
    if(dataType === 'product'){
      dataType = 'products';
    } else if(dataType === 'order'){
      dataType = 'orders';
    } else if(dataType === 'customer'){
      dataType = 'customers';
    } else if(dataType === 'coupon'){
      dataType = 'coupons';
    } else if(dataType === 'category'){
      dataType = 'categories';
    } else if(dataType === 'tag'){
      dataType = 'tags';
    } else if(dataType === 'attribute'){
      dataType = 'attributes';
    } else if(dataType === 'variation'){
      dataType = 'variations';
    } else if(dataType === 'review'){
      dataType = 'reviews';
    } else if(dataType === 'download'){
      dataType = 'downloads';
    }
    
    const credentials = platformCredentials[connectorName];
    if(!credentials){
      alert('Please test connection first.');
      return;
    }

    await fetchAndPreviewPlatformData(connectorName, credentials, dataType);
  });

  // Platform Connectors in Settings
  $(document).on('change', '#mt_primelink-platform-connector-select', async function(){
    const connectorName = $(this).val();
    const $credentials = $('#mt_primelink-platform-connector-credentials');
    const $fields = $('#mt_primelink-platform-connector-fields');
    
    if(!connectorName){
      $credentials.hide();
      return;
    }

    // Ensure connectors are loaded
    if(Object.keys(platformConnectors).length === 0){
      await initPlatformConnector();
    }

    const connector = platformConnectors[connectorName];
    if(!connector){
      $credentials.hide();
      return;
    }

    let fieldsArray = Array.isArray(connector.fields) ? connector.fields : [];
    
    if(fieldsArray.length === 0){
      await initPlatformConnector();
      const updatedConnector = platformConnectors[connectorName];
      if(updatedConnector && Array.isArray(updatedConnector.fields) && updatedConnector.fields.length > 0){
        fieldsArray = updatedConnector.fields;
      } else {
        $credentials.hide();
        return;
      }
    }

    // Render credentials fields
    let html = '';
    fieldsArray.forEach((field) => {
      const fieldKey = field.key || '';
      const fieldId = 'mt_primelink-settings-platform-' + fieldKey.replace(/[^a-z0-9-]/gi, '-');
      const fieldLabel = field.label || fieldKey;
      
      // Try to load saved credentials
      const savedCreds = window.mt_primelinkSettings?.platform_connectors?.[connectorName] || {};
      const savedValue = savedCreds[fieldKey] || '';
      
      html += '<div class="mt_primelink-form-row">';
      html += '<label for="' + fieldId + '">' + escapeHtml(fieldLabel);
      if(field.required) html += ' <span style="color:#f00;">*</span>';
      html += '</label>';
      
      const isPassword = field.type === 'password';
      
      if(isPassword){
        html += '<div style="position:relative;">';
        html += '<input type="password" id="' + fieldId + '" class="widefat" placeholder="' + escapeHtml(field.placeholder || '') + '" data-field="' + escapeHtml(fieldKey) + '" value="' + escapeHtml(savedValue) + '" style="padding-right:80px;">';
        html += '<button type="button" class="button" data-toggle-password="' + fieldId + '" style="position:absolute;right:4px;top:50%;transform:translateY(-50%);height:calc(100% - 8px);padding:0 12px;font-size:12px;line-height:1;">Show</button>';
        html += '</div>';
      } else {
        html += '<input type="text" id="' + fieldId + '" class="widefat" placeholder="' + escapeHtml(field.placeholder || '') + '" data-field="' + escapeHtml(fieldKey) + '" value="' + escapeHtml(savedValue) + '">';
      }
      
      if(field.description){
        html += '<p class="description">' + escapeHtml(field.description) + '</p>';
      }
      html += '</div>';
    });
    
    $fields.html(html);
    $credentials.show();
  });

  $(document).on('click', '#mt_primelink-platform-connector-test', async function(){
    const connectorName = $('#mt_primelink-platform-connector-select').val();
    if(!connectorName){
      alert('Please select a platform first.');
      return;
    }

    const connector = platformConnectors[connectorName];
    if(!connector){
      alert('Connector not found.');
      return;
    }

    // Collect credentials
    const credentials = {};
    $('#mt_primelink-platform-connector-fields input').each(function(){
      const fieldKey = $(this).data('field');
      if(fieldKey){
        credentials[fieldKey] = $(this).val();
      }
    });

    const $btn = $(this);
    const $status = $('#mt_primelink-platform-connector-status');
    $btn.prop('disabled', true).text('Testing...');
    $status.html('');

    try {
      const res = await restPost('/platform/test-connection', {
        connector: connectorName,
        credentials: credentials
      });

      if(res.success){
        $status.html('<span style="color:#0a0;">&#10003; Connection successful!</span>');
        
        // Save credentials to settings
        if(!window.mt_primelinkSettings) window.mt_primelinkSettings = {};
        if(!window.mt_primelinkSettings.platform_connectors || Array.isArray(window.mt_primelinkSettings.platform_connectors)){
          window.mt_primelinkSettings.platform_connectors = {};
        }
        window.mt_primelinkSettings.platform_connectors[connectorName] = credentials;
        
        // Trigger settings save
        $('#mt_primelink-save-settings').click();
      } else {
        $status.html('<span style="color:#f00;">&#10007; ' + escapeHtml(res.message || 'Connection failed') + '</span>');
      }
    } catch(err){
      $status.html('<span style="color:#f00;">&#10007; ' + escapeHtml(err.json?.message || 'Connection failed') + '</span>');
    } finally {
      $btn.prop('disabled', false).text('Test Connection');
    }
  });

  // Load saved platform credentials on settings page load
  $(document).ready(function(){
    if($('#mt_primelink-platform-connector-select').length && window.mt_primelinkSettings?.platform_connectors){
      // Pre-select connector if credentials exist
      const savedConnectors = Object.keys(window.mt_primelinkSettings.platform_connectors);
      if(savedConnectors.length > 0){
        // Could auto-select first saved connector, but for now just ensure they're available
      }
    }
  });

  // Toggle password visibility
  $(document).on('click', '[data-toggle-password]', function(){
    const fieldId = $(this).data('toggle-password');
    const $input = $('#' + fieldId);
    const $btn = $(this);
    
    if($input.attr('type') === 'password'){
      $input.attr('type', 'text');
      $btn.text('Hide');
    } else {
      $input.attr('type', 'password');
      $btn.text('Show');
    }
  });

  // -----------------------------
  // Export Wizard
  // -----------------------------
  function currentExportId(){
    const url = new URL(window.location.href);
    let id = parseInt(url.searchParams.get('export_id') || '0', 10);
    if(!id){
      const $step = $('[data-export-id]').first();
      id = parseInt($step.data('export-id') || '0', 10);
    }
    return id || 0;
  }

  function setExportIdEverywhere(exportId){
    const url = new URL(window.location.href);
    url.searchParams.set('export_id', exportId);
    history.replaceState({}, '', url.toString());

    $('#mt-primelink-next-step, #mt-primelink-finish-export').data('export-id', exportId);
  }

  function getExportStepData(){
    const data = {};
    $('.mt-primelink-wizard__body').find('input, select, textarea').each(function(){
      const $el = $(this);
      const name = $el.attr('name');
      if(!name) return;

      if($el.is(':radio')){
        if($el.is(':checked')) {
          setNestedValue(data, name, $el.val());
        }
      } else if($el.is(':checkbox')){
        if($el.is(':checked')){
          const val = $el.val();
          if(name.indexOf('[]') !== -1){
            // Handle array checkboxes like filters[categories][]
            const nameBase = name.replace(/\[\]$/, '');
            if(!getNestedValue(data, nameBase)){
              setNestedValue(data, nameBase, []);
            }
            const arr = getNestedValue(data, nameBase);
            arr.push(val);
          } else {
            setNestedValue(data, name, 1);
          }
        }
      } else {
        setNestedValue(data, name, $el.val());
      }
    });
    return data;
  }

  function setNestedValue(obj, path, value){
    const keys = path.split(/[\[\]]/).filter(k => k);
    let current = obj;
    for(let i = 0; i < keys.length - 1; i++){
      const key = keys[i];
      if(!current[key] || typeof current[key] !== 'object'){
        current[key] = {};
      }
      current = current[key];
    }
    current[keys[keys.length - 1]] = value;
  }

  function getNestedValue(obj, path){
    const keys = path.split(/[\[\]]/).filter(k => k);
    let current = obj;
    for(let i = 0; i < keys.length; i++){
      if(!current || typeof current !== 'object') return undefined;
      current = current[keys[i]];
    }
    return current;
  }

  // Export wizard next step
  $(document).on('click', '#mt-primelink-next-step', async function(e){
    e.preventDefault();

    const step = getStep();
    const nextStep = step + 1;
    let exportId = parseInt($(this).data('export-id'), 10) || currentExportId();

    try {
      if(!exportId){
        const draft = await restPost('/export/create-draft', {});
        exportId = draft.export_id;
        setExportIdEverywhere(exportId);
      }

      // For step 2, use mapping collection (similar to import step 5)
      const payload = (step === 2) ? collectExportMappingState() : getExportStepData();
      if (step === 2) {
        const mapping = payload.mapping || {};
        if (Object.keys(mapping).length === 0) {
          showToastOrAlert('warning', 'Map at least one field before continuing.', 'Export');
          return;
        }
      }
      
      // Auto-set export_type for step 1 based on export_type from URL
      if(step === 1){
        const url = new URL(window.location.href);
        const exportType = url.searchParams.get('export_type') || '';
        if(exportType){
          payload.export_type = exportType;
        }
      }

      await restPost('/export/save-step', {
        export_id: exportId,
        step: step,
        data: payload
      });

      const url = new URL(window.location.href);
      url.searchParams.set('step', nextStep);
      url.searchParams.set('export_id', exportId);
      window.location.href = url.toString();

    } catch(err){
      showToastOrAlert('error', 'Save failed: ' + (err.json?.message || err.status || 'unknown'), 'Export');
    }
  });

  // Export mapping state collection (similar to import step 5)
  function collectExportMappingState(){
    const mapping = {};
    
    // Collect from standard slots
    $('.mt_primelink-export-step-2 .mt_primelink-map-slot').each(function(){
      const target = $(this).data('target');
      const source = $(this).find('.mt_primelink-map-input').val() || '';
      let cfg = {};
      try {
        cfg = JSON.parse($(this).find('.mt_primelink-map-config').val() || '{}');
      } catch(e){ cfg = {}; }

      if(target && source){
        mapping[target] = { source, config: JSON.stringify(cfg || {}) };
      }
    });
    
    // Also collect from custom columns
    $('#mt_primelink-custom-export-columns-wrap .mt_primelink-map-slot').each(function(){
      const target = $(this).data('target');
      const source = $(this).find('.mt_primelink-map-input').val() || '';
      let cfg = {};
      try {
        cfg = JSON.parse($(this).find('.mt_primelink-map-config').val() || '{}');
      } catch(e){ cfg = {}; }

      if(target && source){
        mapping[target] = { source, config: JSON.stringify(cfg || {}) };
      }
    });

    // ACF export fields
    const acfFields = [];
    $('#mt_primelink-acf-export-fields-wrap .mt_primelink-acf-field').each(function(){
      const $row   = $(this);
      const label  = $row.find('.mt_primelink-acf-label').val() || '';
      const key    = $row.find('.mt_primelink-acf-key').val() || '';
      const source = $row.find('.mt_primelink-map-input').val() || '';
      let cfg = {};
      try {
        cfg = JSON.parse($row.find('.mt_primelink-map-config').val() || '{}');
      } catch(e){ cfg = {}; }

      if (key) {
        acfFields.push({ label, key, source, ...(cfg || {}) });
        if (source) {
          mapping[key] = { source, config: JSON.stringify(cfg || {}) };
        }
      }
    });
    
    return {
      mapping: mapping,
      row_filters: collectFilterRowsData(),
      custom_aliases: collectAliasState(),
      acf_fields: acfFields,
    };
  }

  // Persist export step 2 mapping to draft (debounced)
  let mt_primelinkExportStep2SaveTimer = null;
  function saveExportStep2Mapping(immediate){
    if(mt_primelinkExportStep2SaveTimer){
      clearTimeout(mt_primelinkExportStep2SaveTimer);
    }

    const runner = async () => {
      let exportId = currentExportId() || parseInt($('.mt_primelink-export-step-2').data('export-id'), 10) || 0;
      if(!exportId){
        try{
          const draft = await restPost('/export/create-draft', {});
          exportId = draft.export_id || 0;
          if(exportId){
            setExportIdEverywhere(exportId);
            $('.mt_primelink-export-step-2').attr('data-export-id', exportId);
          }
        }catch(e){
          debugConsole.error('Export ID missing:', e);
          throw e; // Re-throw so caller can handle
        }
      }
      const payload = collectExportMappingState();
      try{
        await restPost('/export/save-step', {
          export_id: exportId,
          step: 2,
          data: payload
        });
      } catch(err){
        debugConsole.error('Save failed:', err);
        throw err; // Re-throw so caller can handle
      }
    };

    if(immediate){
      return runner(); // Return promise for await
    }

    mt_primelinkExportStep2SaveTimer = setTimeout(runner, 100);
    return Promise.resolve(); // Return resolved promise for non-immediate case
  }

  // Auto-save export mapping when changes occur
  $(document).on('change blur', '.mt_primelink-export-step-2 .mt_primelink-map-input, .mt_primelink-export-step-2 .mt_primelink-map-config', function(){
    saveExportStep2Mapping();
  });

  // Also handle custom columns
  $(document).on('change blur', '#mt_primelink-custom-export-columns-wrap .mt_primelink-map-input, #mt_primelink-custom-export-columns-wrap .mt_primelink-map-config', function(){
    saveExportStep2Mapping();
  });

  // Auto-save when drag & drop occurs in export step 2
  $(document).on('drop', '.mt_primelink-export-step-2 .mt-map-slot__drop', function(){
    setTimeout(() => saveExportStep2Mapping(), 100);
  });

  // Add custom export column
  $(document).on('click', '#mt_primelink-add-custom-export-column', function(){
    const existingCount = $('#mt_primelink-custom-export-columns-wrap .mt_primelink-map-slot').length;
    const customId = 'custom_column_' + (existingCount + 1);
    
    const html = 
      '<div class="mt_primelink-map-slot unmapped" data-target="' + escapeHtml(customId) + '">' +
        '<div class="mt_primelink-map-label">' +
          '<span class="mt_primelink-map-label__text" data-editable="true" title="Click to rename">' + escapeHtml(customId) + '</span>' +
          '<input type="text" class="mt_primelink-custom-column-name" style="display:none;width:200px;padding:2px 6px;font-size:13px;" value="' + escapeHtml(customId) + '" />' +
          '<button type="button" class="button-link-delete mt_primelink-map-slot-remove" title="Remove column">&times;</button>' +
        '</div>' +
        '<div class="mt_primelink-map-drop mt-map-slot__drop">' +
          '<span class="mt_primelink-muted">Drop field here</span>' +
        '</div>' +
        '<input type="hidden" class="mt_primelink-map-input" value="" />' +
        '<input type="hidden" class="mt_primelink-map-config" value="" />' +
      '</div>';
    
    $('#mt_primelink-custom-export-columns-wrap').append(html);
    
    // Initialize drag & drop for the new slot
    bindDnD();
    
    // Save mapping
    saveExportStep2Mapping();
  });

  // Remove custom export column
  $(document).on('click', '#mt_primelink-custom-export-columns-wrap .mt_primelink-map-slot-remove', function(){
    $(this).closest('.mt_primelink-map-slot').remove();
    saveExportStep2Mapping();
    loadExportPreview();
  });

  // Edit custom column name (click to edit)
  $(document).on('click', '#mt_primelink-custom-export-columns-wrap .mt_primelink-map-label__text[data-editable="true"]', function(e){
    e.stopPropagation();
    const $text = $(this);
    const $input = $text.siblings('.mt_primelink-custom-column-name');
    const $slot = $text.closest('.mt_primelink-map-slot');
    
    $text.hide();
    $input.show().focus().select();
  });

  // Save custom column name (on blur or Enter)
  $(document).on('blur keydown', '#mt_primelink-custom-export-columns-wrap .mt_primelink-custom-column-name', function(e){
    if(e.type === 'keydown' && e.keyCode !== 13) return; // Only handle Enter key
    
    const $input = $(this);
    const $text = $input.siblings('.mt_primelink-map-label__text');
    const $slot = $input.closest('.mt_primelink-map-slot');
    const newName = $input.val().trim() || $slot.data('target'); // Fallback to target if empty
    
    // Update display text
    $text.text(newName);
    $text.show();
    $input.hide();
    
    // Update config with column_header
    let cfg = {};
    try {
      cfg = JSON.parse($slot.find('.mt_primelink-map-config').val() || '{}');
    } catch(e){ cfg = {}; }
    
    cfg.column_header = newName;
    $slot.find('.mt_primelink-map-config').val(JSON.stringify(cfg));
    
    // Save mapping
    saveExportStep2Mapping();
    loadExportPreview();
  });

  // Select/Deselect all fields
  $(document).on('click', '#mt_primelink-select-all-fields', function(){
    $('input[name="fields[]"]').prop('checked', true);
  });

  $(document).on('click', '#mt_primelink-deselect-all-fields', function(){
  // ACF export fields: add/remove & key sync
  $(document).on('click', '#mt_primelink-add-acf-export-field', function(){
    const i = $('#mt_primelink-acf-export-fields-wrap .mt_primelink-acf-field').length;
    const html =
      '<div class="mt_primelink-acf-field" data-index="'+i+'">' +
        '<div class="mt_primelink-acf-row">' +
          '<input type="text" class="mt_primelink-acf-label" placeholder="ACF Field Label" />' +
          '<input type="text" class="mt_primelink-acf-key" placeholder="ACF meta key (e.g. field_XXXX or my_field)" />' +
        '</div>' +
        '<div class="mt_primelink-map-slot unmapped" data-target="">' +
          '<div class="mt_primelink-map-drop mt-map-slot__drop"><span class="mt_primelink-muted">Drop field here</span></div>' +
        '</div>' +
        '<input type="hidden" class="mt_primelink-map-input" value="" />' +
        '<input type="hidden" class="mt_primelink-map-config" value="" />' +
        '<button type="button" class="button-link-delete mt_primelink-acf-export-remove">Remove</button>' +
      '</div>';
    $('#mt_primelink-acf-export-fields-wrap').append(html);
    bindDnD();
  });

  $(document).on('click', '.mt_primelink-acf-export-remove', function(){
    $(this).closest('.mt_primelink-acf-field').remove();
    saveExportStep2Mapping();
  });

  // When meta key changes, update data-target to key (so mapping saves correctly)
  $(document).on('input change', '#mt_primelink-acf-export-fields-wrap .mt_primelink-acf-key', function(){
    const key = ($(this).val() || '').trim();
    const $field = $(this).closest('.mt_primelink-acf-field');
    const $slot = $field.find('.mt_primelink-map-slot');
    $slot.attr('data-target', key || '');
    // optional: update label placeholder if empty
    const $label = $field.find('.mt_primelink-acf-label');
    if(!$label.val().trim() && key){
      $label.val(key);
    }
    saveExportStep2Mapping();
  });

    $('input[name="fields[]"]').prop('checked', false);
  });

  // Load export preview (step 2)
  async function loadExportPreview(){
    const $preview = $('#mt-primelink-export-preview');
    if(!$preview.length) return;

    const exportId = parseInt($preview.data('export-id'), 10) || currentExportId();
    if(!exportId) {
      $preview.html('<div class="mt_primelink-empty">Please complete step 1 first.</div>');
      return;
    }

    try {
      const res = await restPost('/export/preview', {
        export_id: exportId,
        page: 1,
        per_page: 10
      });

      const headers = res.headers || [];
      const rows = res.rows || [];
      const total = res.total || 0;

      if (!total || rows.length === 0) {
        $preview.html('<div class="mt_primelink-empty">No items found. Please configure filters.</div>');
        return;
      }

      // Get current mapping state
      const mappingState = collectExportMappingState();
      const mapping = mappingState.mapping || {};

      if(Object.keys(mapping).length === 0){
        $preview.html('<div class="mt_primelink-empty">No columns mapped yet. Drag fields from left panel to create export columns.</div>');
        return;
      }

      // Use first row for preview (or index if provided)
      const previewIndex = window.mt_primelinkExportPreviewIndex || 0;
      const currentRow = rows[Math.min(previewIndex, rows.length - 1)] || [];
      const rowNumber = Math.min(previewIndex, rows.length - 1) + 1;

      // Build mapped values - match headers with mapping
      // IMPORTANT: Headers and rows are built in the same order as mapping keys in PHP
      // We need to match by index, but ensure we use the same order as backend
      const mappingKeys = Object.keys(mapping);
      const mapped = {};
      
      // Match by index - headers and rows are in the same order as mapping keys
      mappingKeys.forEach((target, index) => {
        if(index >= headers.length || index >= currentRow.length){
          mapped[target] = '';
          return;
        }
        
        // Get value directly by index (rows are built in same order as headers/mapping keys)
        const value = currentRow[index] || '';
        mapped[target] = value;
      });

      // Render vertical table like import step 5
      let html = '<div class="mt-preview-meta"><strong>Type:</strong> Export</div>';

      // Navigator
      html += '<div class="mt-preview-nav">';
      html += '  <button type="button" class="button mt-export-preview-prev" ' + (previewIndex <= 0 ? 'disabled' : '') + ' data-index="' + Math.max(0, previewIndex-1) + '">&lsaquo; Prev</button>';
      html += '  <span class="mt-preview-counter">Row ' + rowNumber + ' / ' + total + '</span>';
      html += '  <button type="button" class="button button-primary mt-export-preview-next" ' + (previewIndex >= rows.length - 1 ? 'disabled' : '') + ' data-index="' + Math.min(rows.length - 1, previewIndex+1) + '">Next &rsaquo;</button>';
      html += '</div>';

      // Vertical table
      html += '<div class="mt-preview-vertical-wrap">';
      html += '<table class="mt-preview-vertical">';
      html += '<thead><tr><th>Target Field</th><th>Mapped Value</th></tr></thead><tbody>';

      Object.keys(mapping).forEach(target => {
        const config = mapping[target];
        const configObj = typeof config.config === 'string' ? JSON.parse(config.config || '{}') : (config.config || {});
        const customHeader = configObj.column_header || '';
        const fieldLabel = customHeader || (target.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()));
        
        let val = mapped[target] || '';
        const rawVal = val;

        if(Array.isArray(val)){
          val = val.map(v => "- " + v).join("\n");
        } else {
          val = (val === undefined || val === null) ? "" : String(val);
        }

        html += '<tr>';
        html += '  <td class="mt-preview-field">' + escapeHtml(fieldLabel) + '</td>';
        html += '  <td class="mt-preview-value"><pre style="margin:0;white-space:pre-wrap;">' + escapeHtml(val) + '</pre></td>';
        html += '</tr>';
      });

      html += '</tbody></table></div>';
      $preview.html(html);

      // Store index for navigation
      window.mt_primelinkExportPreviewIndex = previewIndex;

    } catch(err){
      $preview.html('<div class="mt_primelink-empty error">Failed to load preview: ' + (err.json?.message || 'Unknown error') + '</div>');
    }
  }

  // Auto-load export preview when step 2 is loaded
  $(document).ready(function(){
    if($('.mt_primelink-export-step-2').length){
      // Load existing custom columns from draft
      loadExportCustomColumns();
      // Load existing ACF export fields
      loadExportACFFields();
      // Wait a bit for mapping to be loaded, then load preview
      setTimeout(() => {
        loadExportPreview();
      }, 500);
    }
  });

  // Load custom columns from draft
  async function loadExportCustomColumns(){
    const exportId = currentExportId();
    if(!exportId) return;

    try {
      const draft = await restPost('/export/get-draft', { export_id: exportId });
      if(!draft || !draft.step_data || !draft.step_data[2]) return;

      const step2Data = draft.step_data[2];
      const mapping = step2Data.mapping || {};
      
      // Find custom columns (those starting with custom_column_)
      const customColumns = {};
      Object.keys(mapping).forEach(key => {
        if(key.startsWith('custom_column_')){
          customColumns[key] = mapping[key];
        }
      });

      if(Object.keys(customColumns).length === 0) return;

      // Clear existing custom columns
      $('#mt_primelink-custom-export-columns-wrap').empty();

      // Add custom columns
      Object.keys(customColumns).forEach(customId => {
        const columnConfig = customColumns[customId];
        const source = columnConfig.source || '';
        let cfg = {};
        try {
          cfg = typeof columnConfig.config === 'string' ? JSON.parse(columnConfig.config || '{}') : (columnConfig.config || {});
        } catch(e){ cfg = {}; }

        // Get custom column name from config or use ID
        const columnName = cfg.column_header || customId;
        
        const html = 
          '<div class="mt_primelink-map-slot' + (source ? '' : ' unmapped') + '" data-target="' + escapeHtml(customId) + '">' +
            '<div class="mt_primelink-map-label">' +
              '<span class="mt_primelink-map-label__text" data-editable="true" title="Click to rename">' + escapeHtml(columnName) + '</span>' +
              '<input type="text" class="mt_primelink-custom-column-name" style="display:none;width:200px;padding:2px 6px;font-size:13px;" value="' + escapeHtml(columnName) + '" />' +
              '<button type="button" class="button-link-delete mt_primelink-map-slot-remove" title="Remove column">&times;</button>' +
            '</div>' +
            '<div class="mt_primelink-map-drop mt-map-slot__drop">' +
              (source ? buildChip(source) : '<span class="mt_primelink-muted">Drop field here</span>') +
            '</div>' +
            '<input type="hidden" class="mt_primelink-map-input" value="' + escapeHtml(source) + '" />' +
            '<input type="hidden" class="mt_primelink-map-config" value="' + escapeHtml(JSON.stringify(cfg || {})) + '" />' +
          '</div>';
        
        $('#mt_primelink-custom-export-columns-wrap').append(html);
      });

      // Initialize drag & drop
      bindDnD();
    } catch(err){
      debugConsole.error('Failed to load custom columns:', err);
    }
  }

  // Load ACF export fields from draft
  async function loadExportACFFields(){
    const exportId = currentExportId();
    if(!exportId) return;

    try {
      const draft = await restPost('/export/get-draft', { export_id: exportId });
      if(!draft || !draft.step_data || !draft.step_data[2]) return;

      const step2Data = draft.step_data[2];
      const acfFields = step2Data.acf_fields || [];
      if(!acfFields.length) return;

      const wrap = $('#mt_primelink-acf-export-fields-wrap');
      if(!wrap.length) return;

      wrap.empty();
      acfFields.forEach((acf, index) => {
        const hasSource = acf.source && acf.source.trim();
        const unmappedClass = hasSource ? '' : ' unmapped';
        const dropHtml = hasSource
          ? buildChip(acf.source)
          : '<span class="mt_primelink-muted">Drop field here</span>';

        const html =
          '<div class="mt_primelink-acf-field" data-index="'+index+'">' +
            '<div class="mt_primelink-acf-row">' +
              '<input type="text" class="mt_primelink-acf-label" placeholder="ACF Field Label" value="'+escapeHtml(acf.label || '')+'" />' +
              '<input type="text" class="mt_primelink-acf-key" placeholder="ACF meta key (e.g. field_XXXX or my_field)" value="'+escapeHtml(acf.key || '')+'" />' +
            '</div>' +
            '<div class="mt_primelink-map-slot'+unmappedClass+'" data-target="'+escapeHtml(acf.key || '')+'">' +
              '<div class="mt_primelink-map-drop mt-map-slot__drop">' + dropHtml + '</div>' +
            '</div>' +
            '<input type="hidden" class="mt_primelink-map-input" value="'+escapeHtml(acf.source || '')+'" />' +
            '<input type="hidden" class="mt_primelink-map-config" value="'+escapeHtml(JSON.stringify(acf.config || {}))+'" />' +
            '<button type="button" class="button-link-delete mt_primelink-acf-export-remove">Remove</button>' +
          '</div>';
        wrap.append(html);
      });

      bindDnD();
    } catch(err){
      debugConsole.error('Failed to load ACF export fields:', err);
    }
  }

  // Reload preview when mapping changes
  $(document).on('change drop', '.mt_primelink-export-step-2 .mt_primelink-map-input, .mt_primelink-export-step-2 .mt-map-slot__drop', function(){
    setTimeout(() => {
      saveExportStep2Mapping();
      window.mt_primelinkExportPreviewIndex = 0; // Reset to first row
      loadExportPreview();
    }, 300);
  });

  // Export preview navigation
  $(document).on('click', '.mt-export-preview-prev, .mt-export-preview-next', function(){
    const index = parseInt($(this).data('index'), 10) || 0;
    window.mt_primelinkExportPreviewIndex = index;
    loadExportPreview();
  });

  // Reload preview when mapping is cleared (for export step 2)
  $(document).on('click', '.mt_primelink-export-step-2 .mt_primelink-suggestion-clear, .mt_primelink-export-step-2 #mt_primelink-clear-mapping', function(){
    setTimeout(() => {
      saveExportStep2Mapping();
      loadExportPreview();
    }, 300);
  });

  // Reload preview when slot is cleared manually
  $(document).on('click', '.mt_primelink-export-step-2 .mt-map-slot__drop .mt_primelink-chip', function(e){
    if(e.ctrlKey || e.metaKey){
      e.preventDefault();
      const $slot = $(this).closest('.mt_primelink-map-slot');
      $slot.find('.mt-map-slot__drop').html('<span class="mt_primelink-muted">Drop field here</span>');
      $slot.find('.mt_primelink-map-input').val('');
      $slot.find('.mt_primelink-map-config').val('');
      $slot.find('.mt_primelink-map-edit').prop('disabled', true);
      setTimeout(() => {
        saveExportStep2Mapping();
        loadExportPreview();
      }, 300);
    }
  });

  // Auto-map for export (similar to import but uses WooCommerce fields)
  function autoMapExportSuggestions(){
    // Get available WooCommerce fields from the left panel
    const sources = [];
    $('.mt_primelink-export-step-2 .mt_primelink-file-field').each(function(){
      const field = $(this).data('field') || $(this).find('.mt_primelink-file-field__name').text().trim();
      if(field){
        sources.push(field);
      }
    });

    if(!sources.length){
      showToastOrAlert('warning', 'No WooCommerce fields available.', 'Mapping');
      return;
    }

    const norm = s => (s || '').toString().toLowerCase().replace(/[^a-z0-9]+/g, '');

    // Target aliases for export columns (matching WooCommerce field names)
    const targetAliases = {
      id:                ['id','productid','product_id'],
      title:             ['title','name','productname','product_title'],
      sku:               ['sku','cod','code'],
      price:             ['price','regularprice','regular_price'],
      sale_price:        ['saleprice','sale_price','discountprice'],
      stock_quantity:    ['stock','stockquantity','stock_quantity','qty','quantity'],
      stock_status:      ['stockstatus','stock_status'],
      description:       ['description','desc','content'],
      short_description: ['shortdescription','short_desc','excerpt'],
      categories:        ['categories','category','cats'],
      tags:              ['tags','tag'],
      featured_image:    ['image','featuredimage','featured_image','mainimage'],
      gallery_images:    ['gallery','galleryimages','gallery_images','images'],
    };

    // Merge custom aliases
    $('.mt_primelink-alias-row').each(function(){
      const target = ($(this).find('.mt_primelink-alias-target').val() || '').trim();
      const vals = ($(this).find('.mt_primelink-alias-values').val() || '').split(',').map(v=>v.trim()).filter(Boolean);
      if(!target || !vals.length) return;
      if(!targetAliases[target]) targetAliases[target] = [];
      targetAliases[target] = targetAliases[target].concat(vals.map(v=>norm(v)));
    });

    const normalizedSources = sources.map(s => ({raw:s, n:norm(s)}));

    function similarity(a,b){
      const la = a.length, lb = b.length;
      if(!la || !lb) return 0;
      const dp = Array.from({length: la+1}, () => new Array(lb+1).fill(0));
      for(let i=0;i<=la;i++) dp[i][0]=i;
      for(let j=0;j<=lb;j++) dp[0][j]=j;
      for(let i=1;i<=la;i++){
        for(let j=1;j<=lb;j++){
          const cost = a[i-1] === b[j-1] ? 0 : 1;
          dp[i][j] = Math.min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+cost);
        }
      }
      const dist = dp[la][lb];
      return 1 - (dist / Math.max(la, lb));
    }

    function findBestMatch(target){
      const nTarget = norm(target);
      let best = { score: 0, source: null };

      // Exact match first
      normalizedSources.forEach(({raw,n}) => {
        if(n === nTarget && best.score < 1){
          best = { score: 1, source: raw };
        }
      });
      if(best.source) return best;

      // Alias-based
      const aliases = targetAliases[target] || [];
      normalizedSources.forEach(({raw,n}) => {
        if(aliases.includes(n)){
          best = { score: 0.99, source: raw };
        }
      });
      if(best.source) return best;

      // Fuzzy match
      normalizedSources.forEach(({raw,n}) => {
        const score = similarity(n, nTarget);
        if(score > best.score){
          best = { score, source: raw };
        }
      });
      if(best.score >= 0.65){
        return best;
      }
      return { score:0, source:null };
    }

    function buildChip(field){
      const $field = $('.mt_primelink-export-step-2 .mt_primelink-file-field[data-field="' + field + '"]');
      const label = $field.find('.mt_primelink-file-field__name').text() || field;
      const safe = field || '(unknown)';
      return '<span class="mt_primelink-chip" data-source="'+escapeHtml(safe)+'">'+escapeHtml(label)+' <a href="#" class="mt_primelink-chip-remove" aria-label="Remove">x</a><span class="mt_primelink-chip-edit-label">edit</span></span>';
    }

    let mappedCount = 0;
    $('.mt_primelink-export-step-2 .mt_primelink-map-slot').each(function(){
      const $slot = $(this);
      const target = $slot.data('target') || '';
      const $input = $slot.find('.mt_primelink-map-input');
      if(($input.val() || '').trim()){
        return; // already mapped
      }

      const match = findBestMatch(target);
      if(match.source){
        $slot.find('.mt-map-slot__drop').html(buildChip(match.source));
        $input.val(match.source);
        // Enable edit button and make it visible
        $slot.find('.mt_primelink-map-edit').prop('disabled', false).addClass('is-visible');
        mappedCount++;
      }
    });

    if(mappedCount){
      // Save mapping and wait for it to complete before loading preview
      (async () => {
        try {
          await saveExportStep2Mapping(true); // immediate save (returns promise)
          // Reload preview after save completes
          await loadExportPreview();
          showToastOrAlert('success', 'Auto-map applied to ' + mappedCount + ' columns.', 'Auto-Map Applied');
        } catch(err) {
          debugConsole.error('Auto-map save/preview error:', err);
          showToastOrAlert('warning', 'Auto-map applied to ' + mappedCount + ' columns, but preview may need a refresh.', 'Auto-Map');
        }
      })();
    } else {
      showToastOrAlert('info', 'No auto-map matches found.', 'Auto-Map');
    }
  }

  // -----------------------------
  // Export Templates (Save/Load/List/Delete)
  // -----------------------------

  async function refreshExportTemplateList(selectNameToKeep){
    const $select = $('#mt_primelink-load-template');
    if(!$select.length) return;

    // Get export type using helper function
    const exportType = await getExportType();
    
    // If exportType is undefined or empty, don't make the request (would return all templates)
    if(!exportType || exportType === ''){
      return;
    }

    try {
      // Use AJAX instead of REST (simpler and more reliable)
      const res = await $.ajax({
        url: mtPrimeLink.ajaxUrl,
        type: 'GET',
        data: {
          action: 'mt_primelink_export_list_templates',
          type: exportType,
          nonce: mtPrimeLink.ajaxNonce,
          ts: Date.now() // cache buster
        },
        dataType: 'json'
      });

      const names = (res.data && res.data.templates) || [];

      // Clear and rebuild dropdown
      let html = '<option value="">Load existing template...</option>';
      if (names.length > 0) {
      names.forEach(n=>{
        // Remove prefix for display
        const displayName = n.replace(/^(order|product|customer|coupon|category|tag):/, '');
        html += '<option value="'+escapeHtml(n)+'">'+escapeHtml(displayName)+'</option>';
      });
      }

      $select.html(html);

      if(selectNameToKeep){
        $select.val(selectNameToKeep);
      }
    } catch(e){
      // If list fails, keep current options
    }
  }

  function applyExportTemplateToUI(data){
    if(!data || typeof data !== 'object') return;

    const mapping = data.mapping || {};
    const row_filters = data.row_filters || [];
    const custom_aliases = data.custom_aliases || [];

    // Apply mapping to slots
    $('.mt_primelink-export-step-2 .mt_primelink-map-slot, #mt_primelink-custom-export-columns-wrap .mt_primelink-map-slot').each(function(){
      const target = $(this).data('target');
      const cfg = mapping[target] || null;

      const $drop  = $(this).find('.mt-map-slot__drop');
      const $input = $(this).find('.mt_primelink-map-input');
      const $conf  = $(this).find('.mt_primelink-map-config');

      if(cfg && cfg.source){
        $drop.html(buildChip(cfg.source));
        $input.val(cfg.source);
        $conf.val(cfg.config || '{}');
        // Remove unmapped class when source is set
        $(this).removeClass('unmapped');
        // Enable edit button when source is set
        $(this).find('.mt_primelink-map-edit').prop('disabled', false).addClass('is-visible');
      } else {
        $drop.html('<span class="mt_primelink-muted">Drop field here</span>');
        $input.val('');
        $conf.val('{}');
        // Add unmapped class when no source (for custom export columns)
        if($(this).closest('#mt_primelink-custom-export-columns-wrap').length){
          $(this).addClass('unmapped');
        }
        // Disable edit button when no source
        $(this).find('.mt_primelink-map-edit').prop('disabled', true).removeClass('is-visible');
      }
    });

    // Apply row filters
    if(row_filters && row_filters.length > 0){
      // TODO: Apply row filters to UI
    }

    // Apply custom aliases
    const aliasWrap = $('#mt_primelink-alias-wrap').empty();
    if(custom_aliases && custom_aliases.length){
      custom_aliases.forEach(a=>{
        aliasWrap.append(renderAliasRow(a.target || '', (a.aliases || []).join(', ')));
      });
    } else {
      aliasWrap.html('<div class="mt_primelink-empty">No custom aliases yet.</div>');
    }

    bindDnD();
    saveExportStep2Mapping();
    loadExportPreview();
  }

  // Save export template
  $(document).on('click', '#mt_primelink-save-template', async function(){
    // Check if we're on export page
    if(!$('.mt_primelink-export-step-2').length && !$('body').hasClass('mt_primelink-export-wizard')){
      return; // Not on export page, let import handler take over
    }
    const name = ($('#mt_primelink-template-name').val() || '').trim();
    if(!name){
      showToastOrAlert('warning', 'Please enter a template name.', 'Template');
      return;
    }

    // Get export type using helper function
    const exportType = await getExportType();

    const payload = collectExportMappingState();

    try {
      const saved = await restPost('/export/templates/save', {
        name: name,
        data: payload,
        type: exportType
      });

      // Clear input
      $('#mt_primelink-template-name').val('');

      // Refresh template list with a small delay to ensure server has processed the save
      setTimeout(async () => {
        // Ensure we have the export type before refreshing - get it fresh
        const currentExportType = await getExportType();
        if(currentExportType && currentExportType !== ''){
          await refreshExportTemplateList(saved.name || name);
        }
      }, 300);

      showToastOrAlert('success', 'Template saved.', 'Template');
    } catch(e){
      showToastOrAlert('error', 'Template save failed: ' + (e.json?.message || 'Unknown error'), 'Template');
    }
  });

  // Load export template
  // This handler should only run if import handler didn't handle it
  $(document).on('click', '#mt_primelink-load-template-btn', async function(e){
    // Check if we're on export page - be more specific
    const isExportPage = $('.mt_primelink-export-step-2').length > 0 || $('body').hasClass('mt_primelink-export-wizard');
    const isImportPage = $('.mt_primelink-import-step-5').length > 0 || $('body').hasClass('mt_primelink-import-wizard');
    
    // If we're on import page, don't handle it here (let import handler take over)
    if (isImportPage && !isExportPage) {
      return; // Let import handler manage it
    }
    
    // If we're not on export page, don't handle it here
    if(!isExportPage){
      return; // Not on export page, let import handler take over
    }
    
    // Stop propagation to prevent other handlers
    e.preventDefault();
    e.stopPropagation();
    const name = $('#mt_primelink-load-template').val();
    if(!name){
      showToastOrAlert('warning', 'Please select a template.', 'Template');
      return;
    }

    // Ensure mtPrimeLink is available
    if (typeof mtPrimeLink === 'undefined' || !mtPrimeLink.restUrl) {
      showToastOrAlert('error', 'Configuration error: REST API URL not available. Please refresh the page.', 'Template');
      return;
    }

    try {
      const res = await restPost('/export/templates/load', {
        name: name
      });

      // Check if response indicates template not found
      if(res && res.message && (res.message.includes('not found') || res.message.includes('Template not found'))){
        showToastOrAlert('error', 'Template load failed: ' + res.message, 'Template');
        return;
      }

      // WooCommerce REST API returns: { success: true, data: {...} }
      if(res && res.success === true && res.data){
        applyExportTemplateToUI(res.data);
        showToastOrAlert('success', 'Template loaded.', 'Template');
        return; // Exit early to prevent any other code from running
      } else if(res && res.data){
        // Fallback: if data exists directly
        applyExportTemplateToUI(res.data);
        showToastOrAlert('success', 'Template loaded.', 'Template');
        return; // Exit early
      } else if(res && typeof res === 'object' && Object.keys(res).length > 0 && !res.message){
        // Fallback: if response is the data itself (but not an error message)
        applyExportTemplateToUI(res);
        showToastOrAlert('success', 'Template loaded.', 'Template');
        return; // Exit early
      } else {
        showToastOrAlert('error', 'Template load failed: Invalid response.', 'Template');
        return; // Exit early
      }
    } catch(e){
      // Handle 404 responses that contain error messages
      if(e.status === 404 && e.json && e.json.message){
        showToastOrAlert('error', 'Template load failed: ' + e.json.message, 'Template');
        return;
      }
      // Only show error if it's a real error
      const errorMsg = (e.json && e.json.message) || e.message || 'Unknown error';
      showToastOrAlert('error', 'Template load failed: ' + errorMsg, 'Template');
    }
  });

  // Delete export template
  $(document).on('click', '.mt_primelink-export-step-2 #mt_primelink-delete-template', async function(){
    const name = $('#mt_primelink-load-template').val();
    if(!name){
      showToastOrAlert('warning', 'Please select a template to delete.', 'Template');
      return;
    }

    if(!confirm('Are you sure you want to delete this template?')){
      return;
    }

    try {
      await restPost('/export/templates/delete', {
        name: name
      });

      $('#mt_primelink-load-template').val('');
      await refreshExportTemplateList();
      showToastOrAlert('success', 'Template deleted.', 'Template');
    } catch(e){
      showToastOrAlert('error', 'Template delete failed: ' + (e.json?.message || 'Unknown error'), 'Template');
    }
  });

  // Init
  $(document).ready(function(){
    initWizardUI();
    
    // Initialize export wizard if on export page
    if($('body').hasClass('mt_primelink-export-wizard')){
      const exportId = currentExportId();
      if(exportId > 0){
        setExportIdEverywhere(exportId);
      }
      // Load export templates list
      if($('.mt_primelink-export-step-2').length){
        refreshExportTemplateList();
      }
    }
});



  // ============================================
  // Statistics & Analytics Dashboard
  // ============================================

/**
 * Statistics & Analytics Dashboard JavaScript
 *
 * @package PrimeLink
 */

    let charts = {};
    let currentStats = null;
    let currentTab = 'import';
    let currentImportType = '';
    let currentExportType = '';

    // Tab switching
    function switchTab(tab) {
        currentTab = tab;
        $('.mt_primelink-tab-button').removeClass('active');
        $(`.mt_primelink-tab-button[data-tab="${tab}"]`).addClass('active');
        $('.mt_primelink-tab-content').hide();
        $(`#tab-${tab}`).show();
        
        // Load statistics for the active tab
        if (tab === 'import') {
            const dateFrom = $('#mt_primelink-stats-date-from').val() || '';
            const dateTo = $('#mt_primelink-stats-date-to').val() || '';
            loadStatistics(dateFrom, dateTo, currentImportType);
        } else {
            const dateFrom = $('#mt_primelink-export-stats-date-from').val() || '';
            const dateTo = $('#mt_primelink-export-stats-date-to').val() || '';
            loadExportStatistics(dateFrom, dateTo, currentExportType);
        }
    }

    // Load statistics
    async function loadStatistics(dateFrom, dateTo, type = '') {
        // Check if mtPrimeLink is defined
        if (typeof mtPrimeLink === 'undefined') {
            debugConsole.error('mtPrimeLink is not defined');
            alert('Failed to load statistics: Configuration error. Please refresh the page.');
            return;
        }

        // Show loading state
        $('.mt_primelink-statistics__overview, .mt_primelink-statistics__charts, .mt_primelink-statistics__details, .mt_primelink-statistics__tables').css('opacity', '0.5');

        try {
            const res = await restPost('/import/statistics', {
                date_from: dateFrom || '',
                date_to: dateTo || '',
                type: type || '',
            });

            if (!res || typeof res !== 'object') {
                throw new Error('Invalid response from server');
            }

            currentStats = res;
            renderStatistics(res);
            
            // Hide loading state
            $('.mt_primelink-statistics__overview, .mt_primelink-statistics__charts, .mt_primelink-statistics__details, .mt_primelink-statistics__tables').css('opacity', '1');
        } catch(err) {
            
            // Hide loading state
            $('.mt_primelink-statistics__overview, .mt_primelink-statistics__charts, .mt_primelink-statistics__details, .mt_primelink-statistics__tables').css('opacity', '1');
            
            let errorMsg = 'Unknown error';
            if (err && typeof err === 'object') {
                if (err.json && err.json.message) {
                    errorMsg = err.json.message;
                } else if (err.message) {
                    errorMsg = err.message;
                } else if (err.status) {
                    errorMsg = 'HTTP ' + err.status;
                } else if (err.error) {
                    errorMsg = err.error;
                }
            } else if (typeof err === 'string') {
                errorMsg = err;
            }
            
            alert('Failed to load statistics: ' + errorMsg + '. Please check the browser console for more details.');
        }
    }

    // Render statistics
    function renderStatistics(stats) {
        // Overview stats
        $('#stat-total-imports').text(stats.overview.total_imports || 0);
        $('#stat-total-created').text(stats.overview.total_created || 0);
        $('#stat-total-updated').text(stats.overview.total_updated || 0);
        $('#stat-total-skipped').text(stats.overview.total_skipped || 0);
        $('#stat-total-errors').text(stats.overview.total_errors || 0);
        $('#stat-success-rate').text((stats.overview.success_rate || 0) + '%');

        // Performance metrics
        $('#stat-avg-runtime').text(formatDuration(stats.performance.avg_runtime || 0));
        $('#stat-total-runtime').text(formatDuration(stats.performance.total_runtime || 0));
        $('#stat-avg-speed').text((stats.performance.avg_speed || 0).toFixed(2) + ' items/sec');
        $('#stat-min-runtime').text(formatDuration(stats.performance.min_runtime || 0));
        $('#stat-max-runtime').text(formatDuration(stats.performance.max_runtime || 0));

        // Render charts only if Chart.js is available
        // Function to check and render charts
        function checkAndRenderCharts() {
        // Try multiple ways to access Chart.js
        let ChartLib = null;
            
            // Check if Chart is available globally
        if (typeof Chart !== 'undefined') {
            ChartLib = Chart;
        } else if (typeof window.Chart !== 'undefined') {
            ChartLib = window.Chart;
            } else if (window.chartjs && typeof window.chartjs.Chart !== 'undefined') {
                ChartLib = window.chartjs.Chart;
            } else {
                // Check if script tag exists and try to access it
                const chartScripts = document.querySelectorAll('script[src*="chart"]');
                if (chartScripts.length > 0) {
                    // Script is loading or loaded, wait a bit more
                }
            }
            
            if (ChartLib && typeof ChartLib.register !== 'undefined') {
                // Valid Chart.js library found
            // Temporarily assign to global Chart for chart functions
            if (typeof Chart === 'undefined') {
                window.Chart = ChartLib;
            }
                try {
            renderTimelineChart(stats.timeline || []);
            renderStatusChart(stats.by_status || {});
            renderTypesChart(stats.by_type || {});
                    return true;
                } catch (e) {
                    debugConsole.error('Error rendering charts:', e);
                    return false;
                }
            }
            return false;
        }
        
        // Try to render immediately
        if (!checkAndRenderCharts()) {
            // Wait for Chart.js to load (check multiple times with increasing delays)
            let attempts = 0;
            const maxAttempts = 10;
            const checkInterval = setInterval(function() {
                attempts++;
                if (checkAndRenderCharts()) {
                    clearInterval(checkInterval);
                } else if (attempts >= maxAttempts) {
                    clearInterval(checkInterval);
                    
            // Show message to user
            $('.mt_primelink-statistics__charts').prepend(
                '<div class="notice notice-warning" style="padding:10px;margin-bottom:10px;">' +
                        'Charts are not available. Chart.js library is not loaded. Please check the browser console for details and refresh the page.' +
                '</div>'
            );
                }
            }, 100); // Check every 100ms
        }

        // Render tables
        renderTopImports(stats.top_imports || []);
        renderRecentActivity(stats.recent_activity || []);
    }

    // Format duration in seconds to human readable
    function formatDuration(seconds) {
        if (!seconds || seconds < 60) return seconds + 's';
        if (seconds < 3600) return Math.floor(seconds / 60) + 'm ' + (seconds % 60) + 's';
        const hours = Math.floor(seconds / 3600);
        const mins = Math.floor((seconds % 3600) / 60);
        return hours + 'h ' + mins + 'm';
    }

    // Render timeline chart
    function renderTimelineChart(timeline) {
        const ctx = document.getElementById('chart-timeline');
        if (!ctx) return;

        // Get Chart library - try multiple ways
        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        if (!ChartLib) {
            debugConsole.error('Chart.js is not available for timeline chart');
            return;
        }

        const labels = timeline.map(t => t.date);
        const created = timeline.map(t => t.created || 0);
        const updated = timeline.map(t => t.updated || 0);
        const errors = timeline.map(t => t.errors || 0);

        if (charts.timeline) {
            charts.timeline.destroy();
        }

        charts.timeline = new ChartLib(ctx, {
            type: 'line',
            data: {
                labels: labels,
                datasets: [
                    {
                        label: 'Created',
                        data: created,
                        borderColor: 'rgb(16, 185, 129)',
                        backgroundColor: 'rgba(16, 185, 129, 0.1)',
                        tension: 0.4
                    },
                    {
                        label: 'Updated',
                        data: updated,
                        borderColor: 'rgb(59, 130, 246)',
                        backgroundColor: 'rgba(59, 130, 246, 0.1)',
                        tension: 0.4
                    },
                    {
                        label: 'Errors',
                        data: errors,
                        borderColor: 'rgb(239, 68, 68)',
                        backgroundColor: 'rgba(239, 68, 68, 0.1)',
                        tension: 0.4
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        position: 'top',
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
    }

    // Render status chart
    function renderStatusChart(statusData) {
        const ctx = document.getElementById('chart-status');
        if (!ctx) return;

        // Get Chart library - try multiple ways
        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        if (!ChartLib) {
            debugConsole.error('Chart.js is not available for status chart');
            return;
        }

        const labels = Object.keys(statusData);
        const data = Object.values(statusData);

        if (charts.status) {
            charts.status.destroy();
        }

        charts.status = new ChartLib(ctx, {
            type: 'doughnut',
            data: {
                labels: labels.map(l => l.charAt(0).toUpperCase() + l.slice(1)),
                datasets: [{
                    data: data,
                    backgroundColor: [
                        'rgba(156, 163, 175, 0.8)',
                        'rgba(59, 130, 246, 0.8)',
                        'rgba(251, 191, 36, 0.8)',
                        'rgba(16, 185, 129, 0.8)',
                        'rgba(239, 68, 68, 0.8)'
                    ]
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        position: 'bottom',
                    }
                }
            }
        });
    }

    // Render types chart
    function renderTypesChart(typesData) {
        const ctx = document.getElementById('chart-types');
        if (!ctx) return;

        // Get Chart library - try multiple ways
        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        if (!ChartLib) {
            debugConsole.error('Chart.js is not available for types chart');
            return;
        }

        const labels = Object.keys(typesData);
        const counts = Object.values(typesData);

        if (charts.types) {
            charts.types.destroy();
        }

        charts.types = new ChartLib(ctx, {
            type: 'bar',
            data: {
                labels: labels.map(l => l.charAt(0).toUpperCase() + l.slice(1)),
                datasets: [{
                    label: 'Import Count',
                    data: counts,
                    backgroundColor: 'rgba(59, 130, 246, 0.8)'
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        display: false
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
    }

    // Render top imports table
    function renderTopImports(imports) {
        const $container = $('#stat-top-imports');
        if (!imports.length) {
            $container.html('<div class="mt_primelink-empty">No imports found.</div>');
            return;
        }

        let html = '<table class="mt_primelink-dash-table"><thead><tr>';
        html += '<th>ID</th><th>Type</th><th>Created</th><th>Updated</th><th>Errors</th><th>Status</th>';
        html += '</tr></thead><tbody>';

        imports.forEach(imp => {
            html += '<tr>';
            html += '<td>#' + imp.id + '</td>';
            html += '<td>' + escapeHtml(imp.type || 'product') + '</td>';
            html += '<td>' + (imp.created || 0) + '</td>';
            html += '<td>' + (imp.updated || 0) + '</td>';
            html += '<td>' + (imp.errors || 0) + '</td>';
            html += '<td><span class="mt_primelink-status-pill mt_primelink-status-' + escapeHtml(imp.status) + '">' + escapeHtml(imp.status.charAt(0).toUpperCase() + imp.status.slice(1)) + '</span></td>';
            html += '</tr>';
        });

        html += '</tbody></table>';
        $container.html(html);
    }

    // Render recent activity table
    function renderRecentActivity(activity) {
        const $container = $('#stat-recent-activity');
        if (!activity.length) {
            $container.html('<div class="mt_primelink-empty">No recent activity.</div>');
            return;
        }

        let html = '<table class="mt_primelink-dash-table"><thead><tr>';
        html += '<th>ID</th><th>Type</th><th>Created</th><th>Updated</th><th>Errors</th><th>Last Update</th>';
        html += '</tr></thead><tbody>';

        activity.forEach(act => {
            html += '<tr>';
            html += '<td>#' + act.id + '</td>';
            html += '<td>' + escapeHtml(act.type || 'product') + '</td>';
            html += '<td>' + (act.created || 0) + '</td>';
            html += '<td>' + (act.updated || 0) + '</td>';
            html += '<td>' + (act.errors || 0) + '</td>';
            html += '<td>' + escapeHtml(act.last_update || '-') + '</td>';
            html += '</tr>';
        });

        html += '</tbody></table>';
        $container.html(html);
    }

    // Helper function
    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    // Initialize on document ready (only on statistics page)
    $(document).ready(function() {
        // Only run on statistics page
        if (!$('.mt_primelink-statistics').length) {
            return;
        }

        // Check if mtPrimeLink is defined
        if (typeof mtPrimeLink === 'undefined') {
            debugConsole.error('mtPrimeLink is not defined. Make sure the script is properly enqueued.');
            alert('Failed to load statistics: Configuration error. Please refresh the page.');
            return;
        }

        // Event handlers
        $('#mt_primelink-stats-apply-filter').on('click', function() {
            const dateFrom = $('#mt_primelink-stats-date-from').val();
            const dateTo = $('#mt_primelink-stats-date-to').val();
            loadStatistics(dateFrom, dateTo);
        });

        $('#mt_primelink-stats-reset-filter').on('click', function() {
            $('#mt_primelink-stats-date-from').val('');
            $('#mt_primelink-stats-date-to').val('');
            loadStatistics('', '');
        });

        // Chart.js should be loaded as a dependency, but check anyway
        // Try multiple ways to access Chart.js
        let ChartAvailable = typeof Chart !== 'undefined';
        if (!ChartAvailable && typeof window.Chart !== 'undefined') {
            ChartAvailable = true;
        }
        if (!ChartAvailable && typeof window.chartjs !== 'undefined') {
            ChartAvailable = true;
        }
        
        if (!ChartAvailable) {
            // Chart.js is not available, charts will not be rendered
        }

        // Tab switching handlers
        $('.mt_primelink-tab-button').on('click', function() {
            const tab = $(this).data('tab');
            switchTab(tab);
        });

        // Type filter handlers for import
        $('#mt_primelink-import-type-filter').on('change', function() {
            currentImportType = $(this).val() || '';
            const dateFrom = $('#mt_primelink-stats-date-from').val() || '';
            const dateTo = $('#mt_primelink-stats-date-to').val() || '';
            loadStatistics(dateFrom, dateTo, currentImportType);
        });

        // Type filter handlers for export
        $('#mt_primelink-export-type-filter').on('change', function() {
            currentExportType = $(this).val() || '';
            const dateFrom = $('#mt_primelink-export-stats-date-from').val() || '';
            const dateTo = $('#mt_primelink-export-stats-date-to').val() || '';
            loadExportStatistics(dateFrom, dateTo, currentExportType);
        });

        // Export filter handlers
        $('#mt_primelink-export-stats-apply-filter').on('click', function() {
            const dateFrom = $('#mt_primelink-export-stats-date-from').val() || '';
            const dateTo = $('#mt_primelink-export-stats-date-to').val() || '';
            loadExportStatistics(dateFrom, dateTo, currentExportType);
        });

        $('#mt_primelink-export-stats-reset-filter').on('click', function() {
            $('#mt_primelink-export-stats-date-from').val('');
            $('#mt_primelink-export-stats-date-to').val('');
            $('#mt_primelink-export-type-filter').val('');
            currentExportType = '';
            loadExportStatistics('', '', '');
        });

        // Update import filter handler to include type
        $('#mt_primelink-stats-apply-filter').on('click', function() {
            const dateFrom = $('#mt_primelink-stats-date-from').val();
            const dateTo = $('#mt_primelink-stats-date-to').val();
            loadStatistics(dateFrom, dateTo, currentImportType);
        });

        $('#mt_primelink-stats-reset-filter').on('click', function() {
            $('#mt_primelink-stats-date-from').val('');
            $('#mt_primelink-stats-date-to').val('');
            $('#mt_primelink-import-type-filter').val('');
            currentImportType = '';
            loadStatistics('', '', '');
        });

        // Initial load
        loadStatistics('', '', '');
    });

    // Load export statistics
    async function loadExportStatistics(dateFrom, dateTo, type = '') {
        if (typeof mtPrimeLink === 'undefined') {
            debugConsole.error('mtPrimeLink is not defined');
            alert('Failed to load statistics: Configuration error. Please refresh the page.');
            return;
        }

        // Show loading state
        $('#tab-export .mt_primelink-statistics__overview, #tab-export .mt_primelink-statistics__charts, #tab-export .mt_primelink-statistics__details, #tab-export .mt_primelink-statistics__tables').css('opacity', '0.5');

        try {
            const params = new URLSearchParams({
                date_from: dateFrom || '',
                date_to: dateTo || '',
                type: type || '',
            });
            
            const res = await restGet('/export/statistics?' + params.toString());

            if (!res || typeof res !== 'object') {
                throw new Error('Invalid response from server');
            }

            renderExportStatistics(res);
            
            // Hide loading state
            $('#tab-export .mt_primelink-statistics__overview, #tab-export .mt_primelink-statistics__charts, #tab-export .mt_primelink-statistics__details, #tab-export .mt_primelink-statistics__tables').css('opacity', '1');
        } catch(err) {
            debugConsole.error('Failed to load export statistics:', err);
            
            // Hide loading state
            $('#tab-export .mt_primelink-statistics__overview, #tab-export .mt_primelink-statistics__charts, #tab-export .mt_primelink-statistics__details, #tab-export .mt_primelink-statistics__tables').css('opacity', '1');
            
            let errorMsg = 'Unknown error';
            if (err && typeof err === 'object') {
                if (err.json && err.json.message) {
                    errorMsg = err.json.message;
                } else if (err.message) {
                    errorMsg = err.message;
                }
            }
            
            showToastOrAlert('error', 'Failed to load export statistics: ' + errorMsg + '. Please check the browser console for more details.', 'Statistics');
        }
    }

    // Render export statistics
    function renderExportStatistics(stats) {
        // Overview stats
        $('#stat-export-total-exports').text(stats.overview.total_exports || 0);
        $('#stat-export-total-exported').text(stats.overview.total_exported || 0);
        $('#stat-export-total-errors').text(stats.overview.total_errors || 0);
        $('#stat-export-success-rate').text((stats.overview.success_rate || 0) + '%');

        // Performance metrics
        $('#stat-export-avg-runtime').text(formatDuration(stats.performance.avg_runtime || 0));
        $('#stat-export-total-runtime').text(formatDuration(stats.performance.total_runtime || 0));
        $('#stat-export-avg-speed').text((stats.performance.avg_speed || 0).toFixed(2) + ' items/sec');
        $('#stat-export-min-runtime').text(formatDuration(stats.performance.min_runtime || 0));
        $('#stat-export-max-runtime').text(formatDuration(stats.performance.max_runtime || 0));

        // Render charts
        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        
        if (ChartLib) {
            renderExportTimelineChart(stats.timeline || []);
            renderExportStatusChart(stats.by_status || {});
            renderExportTypesChart(stats.by_type || {});
        }

        // Render tables
        renderTopExports(stats.top_exports || []);
        renderExportRecentActivity(stats.recent_activity || []);
    }

    // Render export timeline chart
    function renderExportTimelineChart(timeline) {
        const ctx = document.getElementById('chart-export-timeline');
        if (!ctx) return;

        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        if (!ChartLib) return;

        const labels = timeline.map(t => t.date);
        const exported = timeline.map(t => t.exported || 0);
        const errors = timeline.map(t => t.errors || 0);

        if (charts.exportTimeline) {
            charts.exportTimeline.destroy();
        }

        charts.exportTimeline = new ChartLib(ctx, {
            type: 'line',
            data: {
                labels: labels,
                datasets: [
                    {
                        label: 'Exported',
                        data: exported,
                        borderColor: 'rgb(16, 185, 129)',
                        backgroundColor: 'rgba(16, 185, 129, 0.1)',
                        tension: 0.4
                    },
                    {
                        label: 'Errors',
                        data: errors,
                        borderColor: 'rgb(239, 68, 68)',
                        backgroundColor: 'rgba(239, 68, 68, 0.1)',
                        tension: 0.4
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        position: 'top',
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
    }

    // Render export status chart
    function renderExportStatusChart(statusData) {
        const ctx = document.getElementById('chart-export-status');
        if (!ctx) return;

        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        if (!ChartLib) return;

        const labels = Object.keys(statusData);
        const data = Object.values(statusData);

        if (charts.exportStatus) {
            charts.exportStatus.destroy();
        }

        charts.exportStatus = new ChartLib(ctx, {
            type: 'doughnut',
            data: {
                labels: labels.map(l => l.charAt(0).toUpperCase() + l.slice(1)),
                datasets: [{
                    data: data,
                    backgroundColor: [
                        'rgba(156, 163, 175, 0.8)',
                        'rgba(59, 130, 246, 0.8)',
                        'rgba(251, 191, 36, 0.8)',
                        'rgba(16, 185, 129, 0.8)',
                        'rgba(239, 68, 68, 0.8)'
                    ]
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        position: 'bottom',
                    }
                }
            }
        });
    }

    // Render export types chart
    function renderExportTypesChart(typesData) {
        const ctx = document.getElementById('chart-export-types');
        if (!ctx) return;

        const ChartLib = typeof Chart !== 'undefined' ? Chart : (typeof window.Chart !== 'undefined' ? window.Chart : null);
        if (!ChartLib) return;

        const labels = Object.keys(typesData);
        const counts = Object.values(typesData);

        if (charts.exportTypes) {
            charts.exportTypes.destroy();
        }

        charts.exportTypes = new ChartLib(ctx, {
            type: 'bar',
            data: {
                labels: labels.map(l => l.charAt(0).toUpperCase() + l.slice(1)),
                datasets: [{
                    label: 'Export Count',
                    data: counts,
                    backgroundColor: 'rgba(59, 130, 246, 0.8)'
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        display: false
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
    }

    // Render top exports table
    function renderTopExports(exports) {
        const $container = $('#stat-top-exports');
        if (!exports.length) {
            $container.html('<div class="mt_primelink-empty">No exports found.</div>');
            return;
        }

        let html = '<table class="mt_primelink-dash-table"><thead><tr>';
        html += '<th>ID</th><th>Type</th><th>Exported</th><th>Errors</th><th>Status</th>';
        html += '</tr></thead><tbody>';

        exports.forEach(exp => {
            html += '<tr>';
            html += '<td>#' + exp.id + '</td>';
            html += '<td>' + escapeHtml(exp.type || 'product') + '</td>';
            html += '<td>' + (exp.exported || 0) + '</td>';
            html += '<td>' + (exp.errors || 0) + '</td>';
            html += '<td><span class="mt_primelink-status-pill mt_primelink-status-' + escapeHtml(exp.status) + '">' + escapeHtml(exp.status.charAt(0).toUpperCase() + exp.status.slice(1)) + '</span></td>';
            html += '</tr>';
        });

        html += '</tbody></table>';
        $container.html(html);
    }

    // Render export recent activity table
    function renderExportRecentActivity(activity) {
        const $container = $('#stat-export-recent-activity');
        if (!activity.length) {
            $container.html('<div class="mt_primelink-empty">No recent activity.</div>');
            return;
        }

        let html = '<table class="mt_primelink-dash-table"><thead><tr>';
        html += '<th>ID</th><th>Type</th><th>Exported</th><th>Errors</th><th>Last Update</th>';
        html += '</tr></thead><tbody>';

        activity.forEach(act => {
            html += '<tr>';
            html += '<td>#' + act.id + '</td>';
            html += '<td>' + escapeHtml(act.type || 'product') + '</td>';
            html += '<td>' + (act.exported || 0) + '</td>';
            html += '<td>' + (act.errors || 0) + '</td>';
            html += '<td>' + escapeHtml(act.last_update || '-') + '</td>';
            html += '</tr>';
        });

        html += '</tbody></table>';
        $container.html(html);
    }



  // ============================================
  // Export Management
  // ============================================

/* PrimeLink - Manage Exports
 * Handles export management page functionality
 */

    // Use global restPost from mt-primelink-admin.js
    function restPost(path, body) {
        if (typeof window.restPost === 'function') {
            return window.restPost(path, body);
        }
        debugConsole.error('Global restPost function is not available.');
        throw {status: 500, json: {message: 'restPost not available'}};
    }

    function updateExportRow(exportId, state) {
        const $row = $('.mt_primelink-export-row[data-id="' + exportId + '"]');
        if (!$row.length) return;

        const status = state.status || 'idle';
        const progress = state.progress || 0;
        const current = state.current || 0;
        const total = state.total || 0;

        $row.find('.mt_primelink-status-pill')
            .attr('class', 'mt_primelink-status-pill mt_primelink-status-' + status)
            .text(status.charAt(0).toUpperCase() + status.slice(1));

        $row.find('.mt_primelink-dash-progress__bar').css('width', Math.max(0, Math.min(100, progress)) + '%');
        $row.find('.muted').text(current + ' / ' + total);

        // Toggle buttons
        $row.find('.mt_primelink-export-start').prop('disabled', status === 'running' || status === 'paused');
        $row.find('.mt_primelink-export-pause').prop('disabled', status !== 'running');
        $row.find('.mt_primelink-export-resume').prop('disabled', status !== 'paused');
    }

    async function refreshExportRow(exportId) {
        try {
            const state = await restPost('/export/run/status', { export_id: exportId });
            updateExportRow(exportId, state);
        } catch (e) {
            // Silent fail
        }
    }

    $(document).ready(function() {
        // Tab switching is handled by the global handler above

        // Refresh button
        $('#mt_primelink-exports-refresh').on('click', function() {
            $('.mt_primelink-export-row').each(function() {
                const id = parseInt($(this).data('id'), 10);
                refreshExportRow(id);
            });
        });

        // Export actions
        $(document).on('click', '.mt_primelink-export-start', async function() {
            const exportId = parseInt($(this).data('id'), 10);
            try {
                const state = await restPost('/export/run/start', { export_id: exportId });
                updateExportRow(exportId, state);
            } catch (e) {
                showToastOrAlert('error', e.json?.message || 'Failed to start export.', 'Export');
            }
        });

        $(document).on('click', '.mt_primelink-export-pause', async function() {
            const exportId = parseInt($(this).data('id'), 10);
            try {
                const state = await restPost('/export/run/pause', { export_id: exportId });
                updateExportRow(exportId, state);
            } catch (e) {
                showToastOrAlert('error', e.json?.message || 'Failed to pause export.', 'Export');
            }
        });

        $(document).on('click', '.mt_primelink-export-resume', async function() {
            const exportId = parseInt($(this).data('id'), 10);
            try {
                const state = await restPost('/export/run/resume', { export_id: exportId });
                updateExportRow(exportId, state);
            } catch (e) {
                showToastOrAlert('error', e.json?.message || 'Failed to resume export.', 'Export');
            }
        });

        // Select all and delete selected are handled in mt-primelink-admin.js

        // Auto-refresh every 5 seconds for running exports
        setInterval(function() {
            $('.mt_primelink-export-row').each(function() {
                const status = $(this).find('.mt_primelink-status-pill').text().toLowerCase();
                if (status === 'running' || status === 'paused') {
                    const id = parseInt($(this).data('id'), 10);
                    refreshExportRow(id);
                }
            });
        }, 5000);
    });



  // ============================================
  // Template Manager
  // ============================================

/* PrimeLink - Template Manager
 * Handles template management page functionality
 */
    
    // Verify script loaded - remove after testing
    if (typeof window !== 'undefined') {
        window.templateManagerLoaded = true;
    }

    // Helper function for REST API calls
    async function restPost(path, body) {
        if (typeof mtPrimeLink === 'undefined') {
            debugConsole.error('mtPrimeLink is not defined.');
            throw {status: 500, json: {message: 'mtPrimeLink not available'}};
        }
        const opts = {
            method: 'POST',
            headers: {
                'X-WP-Nonce': mtPrimeLink.restNonce,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(body || {})
        };
        const r = await fetch(mtPrimeLink.restUrl + path, opts);
        const j = await r.json().catch(() => ({}));
        if (!r.ok) throw {status: r.status, json: j};
        return j;
    }

    async function restGet(path) {
        if (typeof mtPrimeLink === 'undefined') {
            debugConsole.error('mtPrimeLink is not defined.');
            throw {status: 500, json: {message: 'mtPrimeLink not available'}};
        }
        const r = await fetch(mtPrimeLink.restUrl + path, {
            method: 'GET',
            headers: { 'X-WP-Nonce': mtPrimeLink.restNonce }
        });
        const j = await r.json().catch(() => ({}));
        if (!r.ok) throw {status: r.status, json: j};
        return j;
    }

    // Helper function
    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    // Initialize on document ready
    $(document).ready(function() {
        // Initialization complete
    });

    // Tab switching is handled by the global handler above

    // Search functionality
    $(document).on('input', '#mt-primelink-template-search', function() {
        const query = $(this).val().toLowerCase();
        $('.mt-primelink-template-row').each(function() {
            const $row = $(this);
            const title = $row.find('td:first strong').text().toLowerCase();
            if (title.indexOf(query) !== -1) {
                $row.show();
            } else {
                $row.hide();
            }
        });
    });

    // Select all - same approach as manage imports
    $(document).on('change', '.mt-primelink-select-all', function() {
        const checked = $(this).is(':checked');
        const type = $(this).data('type') || '';
        
        // Use same approach as manage imports - simple selector with data-type
        if (type) {
            $('.mt-primelink-template-select[data-type="' + type + '"]').prop('checked', checked);
        } else {
            $('.mt-primelink-template-select').prop('checked', checked);
        }
    });

    // Sort functionality
    $(document).on('change', '.mt-primelink-sort-select', function() {
        const type = $(this).data('type') || '';
        const sortBy = $(this).val();
        const $tbody = $('.mt_primelink-tab-panel[data-tab-panel="' + type + '"] tbody');
        const $rows = $tbody.find('.mt-primelink-template-row').toArray();

        $rows.sort(function(a, b) {
            const $a = $(a);
            const $b = $(b);
            let aVal, bVal;

            if (sortBy.startsWith('name-')) {
                aVal = $a.find('td:first strong').text().toLowerCase();
                bVal = $b.find('td:first strong').text().toLowerCase();
                return sortBy === 'name-asc' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
            } else if (sortBy.startsWith('date-')) {
                aVal = $a.find('td:nth-child(3)').text();
                bVal = $b.find('td:nth-child(3)').text();
                return sortBy === 'date-desc' ? bVal.localeCompare(aVal) : aVal.localeCompare(bVal);
            } else if (sortBy.startsWith('fields-')) {
                aVal = parseInt($a.find('td:nth-child(2) .muted').text().split(' ')[0] || '0', 10);
                bVal = parseInt($b.find('td:nth-child(2) .muted').text().split(' ')[0] || '0', 10);
                return sortBy === 'fields-desc' ? bVal - aVal : aVal - bVal;
            }
            return 0;
        });

        $tbody.empty().append($rows);
    });

    // Preview template
    $(document).on('click', '.mt-primelink-template-preview', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        const name = $(this).data('name');
        const $modal = $('#mt-primelink-template-preview-modal');
        const $content = $('#mt-primelink-template-preview-content');

        $modal.show();
        $content.html('<p>Loading...</p>');

        try {
            const res = await restPost('/import/templates/load', { name: name });
            const data = res.data || {};

            let html = '<div class="mt-primelink-template-preview-details">';
            html += '<h3>' + escapeHtml(name.replace(/^(order|product|customer|coupon):/, '')) + '</h3>';

            // Mapped fields
            if (data.mapping && Array.isArray(data.mapping)) {
                const mapped = data.mapping.filter(slot => slot.source && slot.source.trim());
                html += '<h4>Mapped Fields (' + mapped.length + ')</h4>';
                html += '<ul class="mt-primelink-preview-list">';
                mapped.forEach(function(slot) {
                    html += '<li><strong>' + escapeHtml(slot.target || '') + '</strong> â† ' + escapeHtml(slot.source || '') + '</li>';
                });
                html += '</ul>';
            }

            // Aliases
            if (data.aliases && Array.isArray(data.aliases) && data.aliases.length > 0) {
                html += '<h4>Custom Aliases</h4>';
                html += '<ul class="mt-primelink-preview-list">';
                data.aliases.forEach(function(alias) {
                    html += '<li><strong>' + escapeHtml(alias.target || '') + '</strong>: ' + escapeHtml((alias.aliases || []).join(', ')) + '</li>';
                });
                html += '</ul>';
            }

            // Other options
            if (data.other) {
                const other = data.other;
                html += '<h4>Other Options</h4>';
                html += '<ul class="mt-primelink-preview-list">';
                if (other.global_prefix) html += '<li>Global Prefix: ' + escapeHtml(other.global_prefix) + '</li>';
                if (other.global_suffix) html += '<li>Global Suffix: ' + escapeHtml(other.global_suffix) + '</li>';
                if (other.global_trim) html += '<li>Global Trim: Enabled</li>';
                if (other.slugify) html += '<li>Slugify: Enabled</li>';
                if (other.fallback_image) html += '<li>Fallback Image: ' + escapeHtml(other.fallback_image) + '</li>';
                html += '</ul>';
            }

            html += '</div>';
            $content.html(html);
        } catch (e) {
            debugConsole.error('Preview error:', e);
            $content.html('<p class="error">Failed to load template preview: ' + (e.json?.message || 'Unknown error') + '</p>');
        }
    });

    // Close modal
    $(document).on('click', '.mt-primelink-modal__close, .mt-primelink-modal__overlay', function() {
        $('#mt-primelink-template-preview-modal').hide();
    });

    // Load template - show modal to select import
    $(document).on('click', '.mt-primelink-template-load', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        const templateName = $(this).data('name');
        const templateType = $(this).data('type');
        
        // Normalize type
        let importType = templateType;
        if (templateType === 'orders') importType = 'order';
        else if (templateType === 'customers') importType = 'customer';
        else if (templateType === 'coupons') importType = 'coupon';
        else if (templateType === 'products') importType = 'product';
        else if (templateType === 'categories') importType = 'category';
        else if (templateType === 'tags') importType = 'tag';
        else if (templateType === 'attributes') importType = 'attribute';
        else if (templateType === 'variations') importType = 'variation';
        else if (templateType === 'reviews') importType = 'review';
        else if (templateType === 'downloads') importType = 'download';
        
        // Show modal to select import
        showTemplateLoadModal(templateName, importType);
    });
    
    // Function to show template load modal
    async function showTemplateLoadModal(templateName, importType) {
        // Create modal if it doesn't exist
        let $modal = $('#mt-primelink-template-load-modal');
        if ($modal.length === 0) {
            $modal = $('<div id="mt-primelink-template-load-modal" class="mt-primelink-modal" style="display:none;"><div class="mt-primelink-modal__overlay"></div><div class="mt-primelink-modal__content glass"><div class="mt-primelink-modal__header"><h2>Select Import</h2><button type="button" class="mt-primelink-modal__close">&times;</button></div><div class="mt-primelink-modal__body"><p>Select an import to load this template into:</p><select id="mt-primelink-template-load-import-select" style="width:100%;padding:8px;margin:12px 0;border:1px solid rgba(0,0,0,0.1);border-radius:6px;"><option value="">Loading imports...</option></select><p class="description" style="margin-top:8px;">Or <a href="#" id="mt-primelink-template-load-new-import" style="text-decoration:underline;">create a new import</a></p></div><div class="mt-primelink-modal__footer"><button type="button" class="button mt-primelink-modal__close">Cancel</button><button type="button" class="button button-primary" id="mt-primelink-template-load-confirm">Load Template</button></div></div></div>');
            $('body').append($modal);
            
            // Close modal handlers
            $modal.on('click', '.mt-primelink-modal__close, .mt-primelink-modal__overlay', function() {
                $modal.hide();
            });
        }
        
        // Load imports list
        try {
            const importsRes = await restGet('/import/list?type=' + encodeURIComponent(importType) + '&ts=' + Date.now());
            const imports = importsRes.imports || [];
            
            const $select = $('#mt-primelink-template-load-import-select');
            let html = '<option value="">Select an import...</option>';
            
            if (imports.length === 0) {
                html = '<option value="">No imports found. Create a new import instead.</option>';
            } else {
                imports.forEach(imp => {
                    html += '<option value="' + escapeHtml(imp.id) + '">' + escapeHtml(imp.display_name) + '</option>';
                });
            }
            
            $select.html(html);
            
            // Store template name and type for later use
            $modal.data('template-name', templateName);
            $modal.data('import-type', importType);
            
            // Show modal
            $modal.show();
        } catch(e) {
            showToastOrAlert('error', 'Failed to load imports: ' + (e.json?.message || e.message || 'Unknown error'), 'Load Template');
        }
    }
    
    // Confirm load template into selected import
    $(document).on('click', '#mt-primelink-template-load-confirm', async function() {
        const $modal = $('#mt-primelink-template-load-modal');
        const templateName = $modal.data('template-name');
        const importType = $modal.data('import-type');
        const importId = $('#mt-primelink-template-load-import-select').val();
        
        if (!importId) {
            showToastOrAlert('warning', 'Please select an import.', 'Load Template');
            return;
        }
        
        try {
            // Load template data
            let templateRes;
            try {
                templateRes = await restPost('/import/templates/load', { name: templateName });
            } catch(loadError) {
                // If load fails, show error and return
                showToastOrAlert('error', 'Template not found: ' + templateName, 'Load Template');
                return;
            }
            
            // Check if response has error message
            if (templateRes && templateRes.message && templateRes.message.includes('not found')) {
                showToastOrAlert('error', 'Template not found: ' + templateName, 'Load Template');
                return;
            }
            
            // Extract template data - response structure: { name, data, is_import }
            // The 'data' field contains the actual mapping data to save in step 5
            const mappingData = templateRes.data || templateRes || {};
            
            // Get current draft to check if type is set
            const draftRes = await restPost('/import/get-draft', { import_id: parseInt(importId, 10) });
            const draft = draftRes.draft || draftRes || {};
            
            // Normalize import type for URL (products -> products, order -> orders, etc.)
            let urlImportType = importType;
            if (importType === 'product') urlImportType = 'products';
            else if (importType === 'order') urlImportType = 'orders';
            else if (importType === 'customer') urlImportType = 'customers';
            else if (importType === 'coupon') urlImportType = 'coupons';
            else if (importType === 'category') urlImportType = 'categories';
            else if (importType === 'tag') urlImportType = 'tags';
            else if (importType === 'attribute') urlImportType = 'attributes';
            else if (importType === 'variation') urlImportType = 'variations';
            else if (importType === 'review') urlImportType = 'reviews';
            else if (importType === 'download') urlImportType = 'downloads';
            
            // Set import type in step 1 if not already set
            if (!draft.type && (!draft.step_data || !draft.step_data[1] || !draft.step_data[1].type)) {
                await restPost('/import/save-step', {
                    import_id: parseInt(importId, 10),
                    step: 1,
                    data: { type: importType }
                });
            }
            
            // Save template mapping data to import's step 5
            // Use mappingData which contains the actual mapping structure
            await restPost('/import/save-step', {
                import_id: parseInt(importId, 10),
                step: 5,
                data: mappingData
            });
            
            $modal.hide();
            
            // Redirect to import wizard at step 5 with import_type and template parameter for auto-load
            const adminUrl = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) ? mtPrimeLink.adminUrl : '';
            const url = adminUrl + 'admin.php?page=mt-primelink-new-import&import_id=' + importId + '&import_type=' + encodeURIComponent(urlImportType) + '&step=5&template=' + encodeURIComponent(templateName);
            window.location.href = url;
        } catch(e) {
            $modal.hide();
            showToastOrAlert('error', 'Failed to load template: ' + (e.json?.message || e.message || 'Unknown error'), 'Load Template');
        }
    });
    
    // Create new import link
    $(document).on('click', '#mt-primelink-template-load-new-import', function(e) {
        e.preventDefault();
        const $modal = $('#mt-primelink-template-load-modal');
        const templateName = $modal.data('template-name');
        const importType = $modal.data('import-type');
        
        $modal.hide();
        
        // Redirect to new import with template
        const adminUrl = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) ? mtPrimeLink.adminUrl : '';
        const url = adminUrl + 'admin.php?page=mt-primelink-new-import&import_type=' + importType + '&step=5&template=' + encodeURIComponent(templateName);
        window.location.href = url;
    });

    // Duplicate template
    $(document).on('click', '.mt-primelink-template-duplicate', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        const name = $(this).data('name');
        const displayName = name.replace(/^(order|product|customer|coupon):/, '');
        const newName = prompt('Enter a new name for the duplicate:', displayName + ' copy');
        
        if (!newName || !newName.trim()) return;

        try {
            const res = await restPost('/import/templates/load', { name: name });
            
            // Determine type from name
            let templateType = 'product';
            let prefix = 'product:';
            if (name.startsWith('order:')) {
                templateType = 'order';
                prefix = 'order:';
            } else if (name.startsWith('customer:')) {
                templateType = 'customer';
                prefix = 'customer:';
            } else if (name.startsWith('coupon:')) {
                templateType = 'coupon';
                prefix = 'coupon:';
            } else if (name.startsWith('product:')) {
                templateType = 'product';
                prefix = 'product:';
            }

            // Remove any existing prefix from newName and add correct one
            const cleanName = newName.trim().replace(/^(order|product|customer|coupon):/, '');
            const finalName = prefix + cleanName;

            await restPost('/import/templates/save', {
                name: finalName,
                data: res.data,
                type: templateType
            });

            showToastOrAlert('success', 'Template duplicated successfully.', 'Template');
            location.reload();
        } catch (e) {
            debugConsole.error('Duplicate error:', e);
            showToastOrAlert('error', 'Failed to duplicate template: ' + (e.json?.message || 'Unknown error'), 'Template');
        }
    });

    // Delete template
    $(document).on('click', '.mt-primelink-template-delete', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        const name = $(this).data('name');
        const displayName = name.replace(/^(order|product|customer|coupon):/, '');
        
        if (!confirm('Delete template "' + displayName + '"? This cannot be undone.')) {
            return;
        }

        try {
            await restPost('/import/templates/delete', { name: name });
            $(this).closest('.mt-primelink-template-row').fadeOut(300, function() {
                $(this).remove();
            });
        } catch (e) {
            debugConsole.error('Delete error:', e);
            showToastOrAlert('error', 'Failed to delete template: ' + (e.json?.message || 'Unknown error'), 'Template');
        }
    });

    // Bulk delete
    $(document).on('click', '.mt-primelink-bulk-delete', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        const type = $(this).data('type') || '';
        
        // Use same approach as manage imports
        let selector = '.mt-primelink-template-select:checked';
        if (type) {
            selector = '.mt-primelink-template-select[data-type="' + type + '"]:checked';
        }
        const $checked = $(selector);
        
        if ($checked.length === 0) {
            showToastOrAlert('warning', 'Select at least one template to delete.', 'Template');
            return;
        }

        if (!confirm('Delete ' + $checked.length + ' selected template(s)? This cannot be undone.')) {
            return;
        }

        const $button = $(this);
        const originalText = $button.text();
        $button.prop('disabled', true).text('Deleting...');

        try {
            const names = $checked.map(function() {
                return $(this).val();
            }).get();

            for (const name of names) {
                await restPost('/import/templates/delete', { name: name });
                $('.mt-primelink-template-row[data-name="' + name + '"]').fadeOut(300, function() {
                    $(this).remove();
                });
            }
            
            // Uncheck select all
            if (type) {
                $('.mt-primelink-select-all[data-type="' + type + '"]').prop('checked', false);
            }
        } catch (e) {
            debugConsole.error('Bulk delete error:', e);
            showToastOrAlert('error', 'Failed to delete templates: ' + (e.json?.message || 'Unknown error'), 'Template');
        } finally {
            $button.prop('disabled', false).text(originalText);
        }
    });

    // Bulk duplicate
    $(document).on('click', '.mt-primelink-bulk-duplicate', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        const type = $(this).data('type') || '';
        
        // Use same approach as manage imports
        let selector = '.mt-primelink-template-select:checked';
        if (type) {
            selector = '.mt-primelink-template-select[data-type="' + type + '"]:checked';
        }
        const $checked = $(selector);
        
        if ($checked.length === 0) {
            showToastOrAlert('warning', 'Select at least one template to duplicate.', 'Template');
            return;
        }

        const $button = $(this);
        const originalText = $button.text();
        $button.prop('disabled', true).text('Duplicating...');

        try {
            const names = $checked.map(function() {
                return $(this).val();
            }).get();

            for (const name of names) {
                const res = await restPost('/import/templates/load', { name: name });
                const displayName = name.replace(/^(order|product|customer|coupon):/, '');
                
                // Determine type and prefix
                let templateType = 'product';
                let prefix = 'product:';
                if (name.startsWith('order:')) {
                    templateType = 'order';
                    prefix = 'order:';
                } else if (name.startsWith('customer:')) {
                    templateType = 'customer';
                    prefix = 'customer:';
                } else if (name.startsWith('coupon:')) {
                    templateType = 'coupon';
                    prefix = 'coupon:';
                } else if (name.startsWith('product:')) {
                    templateType = 'product';
                    prefix = 'product:';
                }

                // Add prefix to new name
                const newName = prefix + displayName + ' copy';

                await restPost('/import/templates/save', {
                    name: newName,
                    data: res.data,
                    type: templateType
                });
            }

            showToastOrAlert('success', 'Templates duplicated successfully.', 'Template');
            location.reload();
        } catch (e) {
            debugConsole.error('Bulk duplicate error:', e);
            showToastOrAlert('error', 'Failed to duplicate templates: ' + (e.json?.message || 'Unknown error'), 'Template');
        } finally {
            $button.prop('disabled', false).text(originalText);
        }
    });

    // Export all templates
    $(document).on('click', '#mt-primelink-export-all-templates', async function(e) {
        e.preventDefault();
        e.stopPropagation();
        try {
            const res = await restGet('/import/templates');
            const templateNames = res.templates || [];
            
            if (templateNames.length === 0) {
                showToastOrAlert('warning', 'No templates to export.', 'Template');
                return;
            }

            // Load all templates
            const templates = {};
            for (const name of templateNames) {
                try {
                    const templateRes = await restPost('/import/templates/load', { name: name });
                    templates[name] = templateRes.data;
                } catch (e) {
                    debugConsole.error('Failed to load template:', name, e);
                }
            }

            // Create JSON and download
            const json = JSON.stringify(templates, null, 2);
            const blob = new Blob([json], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'primelink-templates-' + new Date().toISOString().split('T')[0] + '.json';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        } catch (e) {
            debugConsole.error('Export error:', e);
            showToastOrAlert('error', 'Failed to export templates: ' + (e.json?.message || 'Unknown error'), 'Template');
        }
    });

    // Import templates
    $(document).on('click', '#mt-primelink-import-templates', function(e) {
        e.preventDefault();
        e.stopPropagation();
        $('#mt-primelink-import-file').click();
    });

    $(document).on('change', '#mt-primelink-import-file', async function() {
        const file = this.files[0];
        if (!file) return;

        const reader = new FileReader();
        reader.onload = async function(e) {
            try {
                const templates = JSON.parse(e.target.result);
                let imported = 0;
                let failed = 0;

                for (const [name, data] of Object.entries(templates)) {
                    try {
                        // Determine type from name
                        let type = '';
                        if (name.startsWith('order:')) type = 'order';
                        else if (name.startsWith('customer:')) type = 'customer';
                        else if (name.startsWith('coupon:')) type = 'coupon';
                        else if (name.startsWith('product:')) type = 'product';
                        else type = 'product'; // Default

                        await restPost('/import/templates/save', {
                            name: name,
                            data: data,
                            type: type
                        });
                        imported++;
                    } catch (err) {
                        debugConsole.error('Failed to import template:', name, err);
                        failed++;
                    }
                }

                showToastOrAlert('success', 'Import completed: ' + imported + ' template(s) imported' + (failed > 0 ? ', ' + failed + ' failed' : '') + '.', 'Template');
                location.reload();
            } catch (e) {
                showToastOrAlert('error', 'Failed to parse import file. Please ensure it is a valid JSON file.', 'Template');
            }
        };
        reader.readAsText(file);
    });



  // ============================================
  // Export Wizard
  // ============================================

/* PrimeLink - Export Wizard
 * Handles export wizard initialization
 */

    $(document).ready(function() {
        // Set body classes for export type and wizard
        const $body = $('body');
        const $exportTypeEl = $('[data-export-type]');
        if ($exportTypeEl.length) {
            const exportType = $exportTypeEl.data('export-type') || '';
            if (exportType) {
                $body.addClass('mt_primelink-export-' + exportType);
            }
        }
        $body.addClass('mt_primelink-export-wizard');

        // Handle import type body classes
        const $importTypeEl = $('[data-import-type]');
        if ($importTypeEl.length) {
            const importType = $importTypeEl.data('import-type') || '';
            if (importType) {
                $body.addClass('mt_primelink-import-' + importType);
            }
        }
    });



  // ============================================
  // Export Steps
  // ============================================

/* PrimeLink - Export Steps
 * Handles export wizard step-specific functionality
 */

    // Step 3: CSV format options visibility
    function initStep3() {
        const $formatSelect = $('#mt_primelink-export-format');
        if (!$formatSelect.length) return;

        $formatSelect.on('change', function() {
            if ($(this).val() === 'csv') {
                $('#mt_primelink-csv-options').slideDown(200);
            } else {
                $('#mt_primelink-csv-options').slideUp(200);
            }
        });

        // Initialize
        $formatSelect.trigger('change');
    }

    // Step 4: Export execution, status checking, and progress updates
    function initStep4() {
        const $step4 = $('.mt_primelink-export-step-4');
        if (!$step4.length) return;

        const exportId = parseInt($step4.data('export-id') || 0, 10);

        function runExport(id, background) {
            const destination = $('input[name="destination"]:checked').val() || 'download';

            $('#mt_primelink-export-status-pill').text('Starting...').removeClass('success error').addClass('warning');
            $('#mt_primelink-export-progress-bar').css('width', '0%');
            $('#mt_primelink-export-progress-text').text('0%');

            // Hide start buttons, show pause button
            $('#mt_primelink-start-export').hide();
            $('#mt_primelink-start-export-bg').hide();
            $('#mt_primelink-pause-export').show();
            $('#mt_primelink-resume-export').hide();

            $.ajax({
                url: mtPrimeLink.restUrl + '/export/run/start',
                method: 'POST',
                beforeSend: function(xhr) {
                    xhr.setRequestHeader('X-WP-Nonce', mtPrimeLink.restNonce);
                },
                data: JSON.stringify({
                    export_id: id,
                    background: background,
                    destination: destination
                }),
                contentType: 'application/json',
                success: function(response) {
                    if (response.success) {
                        if (background) {
                            $('#mt_primelink-export-bg-note').show();
                        }
                        checkExportStatus(id);
                    } else {
                        $('#mt_primelink-export-status-pill').text('Error').addClass('error');
                    }
                },
                error: function(xhr) {
                    $('#mt_primelink-export-status-pill').text('Error').addClass('error');
                    $('#mt_primelink-export-log').html('<div class="error">Failed to start export.</div>');
                    // Show start buttons again on error
                    $('#mt_primelink-start-export').show();
                    $('#mt_primelink-start-export-bg').show();
                    $('#mt_primelink-pause-export').hide();
                    $('#mt_primelink-resume-export').hide();
                }
            });
        }

        function checkExportStatus(id) {
            const interval = setInterval(function() {
                $.ajax({
                    url: mtPrimeLink.restUrl + '/export/run/status',
                    method: 'POST',
                    beforeSend: function(xhr) {
                        xhr.setRequestHeader('X-WP-Nonce', mtPrimeLink.restNonce);
                    },
                    data: JSON.stringify({
                        export_id: id
                    }),
                    contentType: 'application/json',
                    success: function(response) {
                        const progress = response.progress || 0;
                        const current = response.current || 0;
                        const total = response.total || 0;

                        $('#mt_primelink-export-progress-bar').css('width', progress + '%');
                        $('#mt_primelink-export-progress-text').text(progress + '%');
                        $('#mt_primelink-export-progress-count').text(current + ' / ' + total);
                        $('#mt_primelink-export-total').text(total);
                        $('#mt_primelink-export-current').text(current);

                        // Update pause/resume buttons based on status
                        if (response.status === 'running') {
                            $('#mt_primelink-pause-export').show();
                            $('#mt_primelink-resume-export').hide();
                            $('#mt_primelink-start-export').hide();
                            $('#mt_primelink-start-export-bg').hide();
                        } else if (response.status === 'paused') {
                            $('#mt_primelink-pause-export').hide();
                            $('#mt_primelink-resume-export').show();
                            $('#mt_primelink-start-export').hide();
                            $('#mt_primelink-start-export-bg').hide();
                        } else {
                            $('#mt_primelink-pause-export').hide();
                            $('#mt_primelink-resume-export').hide();
                            $('#mt_primelink-start-export').show();
                            $('#mt_primelink-start-export-bg').show();
                        }

                        if (response.status === 'completed') {
                            clearInterval(interval);
                            $('#mt_primelink-export-status-pill').text('Completed').addClass('success');
                            $('#mt_primelink-export-progress-status').text('Completed');
                            $('#mt_primelink-pause-export').hide();
                            $('#mt_primelink-resume-export').hide();

                            // Show download link
                            if (response.file_url) {
                                $('#mt_primelink-download-export-file').attr('href', response.file_url).attr('download', '');
                            } else if (response.file_path) {
                                const downloadUrl = mtPrimeLink.restUrl + '/export/download?export_id=' + id + '&_wpnonce=' + mtPrimeLink.restNonce;
                                $('#mt_primelink-download-export-file').attr('href', downloadUrl);
                            }
                            $('#mt_primelink-export-download-link').show();

                            // Show log if available
                            if (response.log && response.log.length > 0) {
                                const logHtml = response.log.map(log => '<div>' + log + '</div>').join('');
                                $('#mt_primelink-export-log').html(logHtml);
                            }
                        } else if (response.status === 'error') {
                            clearInterval(interval);
                            $('#mt_primelink-export-status-pill').text('Error').addClass('error');
                            $('#mt_primelink-export-log').html('<div class="error">Export failed: ' + (response.error || 'Unknown error') + '</div>');
                            $('#mt_primelink-pause-export').hide();
                            $('#mt_primelink-resume-export').hide();
                        } else if (response.status === 'paused') {
                            $('#mt_primelink-export-progress-status').text('Paused');
                        } else {
                            $('#mt_primelink-export-progress-status').text(response.status || 'Running');
                        }
                    }
                });
            }, 1000);
        }

        $(document).ready(function() {
            // Start export
            $('#mt_primelink-start-export').on('click', function() {
                if (exportId === 0) {
                    alert('Please complete all previous steps first.');
                    return;
                }
                runExport(exportId, false);
            });

            // Start export in background
            $('#mt_primelink-start-export-bg').on('click', function() {
                if (exportId === 0) {
                    alert('Please complete all previous steps first.');
                    return;
                }
                const destination = $('input[name="destination"]:checked').val() || 'download';
                $.ajax({
                    url: mtPrimeLink.restUrl + '/export/run/start',
                    method: 'POST',
                    beforeSend: function(xhr) {
                        xhr.setRequestHeader('X-WP-Nonce', mtPrimeLink.restNonce);
                    },
                    data: JSON.stringify({
                        export_id: exportId,
                        background: true,
                        destination: destination
                    }),
                    contentType: 'application/json',
                    success: function(response) {
                        if (response.success) {
                            $('#mt_primelink-export-bg-note').show();
                            if ($('#mt_primelink-export-bg-notify').is(':checked')) {
                                showToastOrAlert('success', 'Export started in background. You can navigate away.', 'Export');
                            }
                        } else {
                            showToastOrAlert('error', 'Failed to start export in background.', 'Export');
                        }
                    },
                    error: function(xhr) {
                        showToastOrAlert('error', 'Failed to start export in background.', 'Export');
                    }
                });
            });

            // Schedule export
            $('#mt_primelink-schedule-export').on('click', async function() {
                if (exportId === 0) {
                    showToastOrAlert('warning', 'Please complete all previous steps first.', 'Export');
                    return;
                }
                
                const $btn = $(this);
                const originalText = $btn.text();
                $btn.prop('disabled', true).text('Scheduling...');

                try {
                    // Check if schedule is enabled in step 3
                    const step3Data = collectExportStep3State();
                    const scheduleEnabled = step3Data.schedule && step3Data.schedule.enabled;
                    
                    if (!scheduleEnabled) {
                        showToastOrAlert('warning', 'Please enable scheduling in Step 3 (Options) first.', 'Export');
                        $btn.prop('disabled', false).text(originalText);
                        return;
                    }

                    // Save step 3 data first to ensure schedule config is saved
                    await saveExportStep3();

                    // Schedule using step 3 config
                    const res = await restPost('/export/schedule', {
                        export_id: exportId
                    });

                    if (res.scheduled) {
                        const when = res.run_at || 'immediately';
                        const recurring = res.recurring ? ' (recurring: ' + res.recurring + ')' : '';
                        showToastOrAlert('success', 'Export scheduled for ' + when + recurring + '. You can manage it in Manage Exports.', 'Export');
                    } else {
                        showToastOrAlert('error', 'Failed to schedule export.', 'Export');
                    }
                } catch (e) {
                    const error = e.json?.message || 'Failed to schedule export.';
                    alert(error);
                } finally {
                    $btn.prop('disabled', false).text(originalText);
                }
            });
        });
    }

    // Initialize based on which step elements are present
    $(document).ready(function() {
        initStep3();
        initStep4();
    });



  // ============================================
  // Orders Admin
  // ============================================



  async function refreshOrderTemplateList(selectNameToKeep){
    const $select = $('#mt_primelink-load-template');
    if(!$select.length) return;

    try {
      const res = await fetch(mtPrimeLink.restUrl + '/import/templates?type=order&ts=' + Date.now(), {
        method: 'GET',
        headers: { 'X-WP-Nonce': mtPrimeLink.restNonce }
      });
      const data = await res.json();
      const names = data.templates || [];
      let html = '<option value="">Load existing template...</option>';
      names.forEach(n=>{
        // Remove prefix for display
        const displayName = n.replace(/^order:/, '');
        html += '<option value="'+n+'">'+displayName+'</option>';
      });
      $select.html(html);
      if(selectNameToKeep){
        $select.val(selectNameToKeep);
      }
    } catch(e){
      // If list fails, keep current options
    }
  }

  $(document).ready(function(){
    if(document.body.classList.contains('mt_primelink-import-orders')){
      refreshOrderTemplateList();
    }
  });

  // ============================================
  // Export Step 3 Options
  // ============================================

  function collectExportStep3State(){
    const scheduleEnabled = $('#mt_primelink-export-enable-schedule').is(':checked');
    const schedule = {};
    
    if(scheduleEnabled){
      const scheduleType = $('#mt_primelink-export-schedule-type').val() || 'preset';
      schedule.enabled = true;
      schedule.name = $('#mt_primelink-export-schedule-name').val() || '';
      schedule.type = scheduleType;
      
      if(scheduleType === 'preset'){
        schedule.interval = $('#mt_primelink-export-schedule-interval').val() || 'daily';
        schedule.start_time = $('#mt_primelink-export-schedule-start').val() || '00:00';
        
        if(schedule.interval === 'weekly'){
          const days = [];
          $('.mt_primelink-export-schedule-day:checked').each(function(){
            days.push($(this).val());
          });
          schedule.days = days;
        } else if(schedule.interval === 'monthly'){
          schedule.month_days = $('#mt_primelink-export-schedule-month-days').val() || '';
        }
      } else if(scheduleType === 'custom'){
        schedule.cron = $('#mt_primelink-export-schedule-cron').val() || '';
      } else if(scheduleType === 'advanced'){
        const times = [];
        $('.mt_primelink-export-schedule-time-slot').each(function(){
          const time = $(this).val();
          if(time) times.push(time);
        });
        schedule.times = times;
        schedule.weekday_filter = $('#mt_primelink-export-schedule-weekday-filter').val() || '';
      }
      
      schedule.timezone = $('#mt_primelink-export-schedule-timezone').val() || '';
      schedule.start_date = $('#mt_primelink-export-schedule-start-date').val() || '';
      schedule.end_date = $('#mt_primelink-export-schedule-end-date').val() || '';
      schedule.max_runs = $('#mt_primelink-export-schedule-max-runs').val() || '';
      schedule.retry_failure = $('#mt_primelink-export-schedule-retry-failure').is(':checked');
      if(schedule.retry_failure){
        schedule.retry_attempts = parseInt($('#mt_primelink-export-schedule-retry-attempts').val() || '3', 10);
        schedule.retry_delay = parseInt($('#mt_primelink-export-schedule-retry-delay').val() || '30', 10);
      }
      schedule.pause_on_error = $('#mt_primelink-export-schedule-pause-on-error').is(':checked');
    }
    
    const data = {
      format: $('#mt_primelink-export-format').val() || 'csv',
      csv_delimiter: $('#mt_primelink-csv-delimiter').val() || ',',
      csv_encoding: $('#mt_primelink-csv-encoding').val() || 'UTF-8',
      include_headers: $('#mt_primelink-export-include-headers').is(':checked'),
      limit: parseInt($('#mt_primelink-export-limit').val() || '0', 10),
      filters: {
        status: $('#mt_primelink-export-status').val() || 'publish',
        categories: $('#mt_primelink-export-categories').val() || [],
        tags: $('#mt_primelink-export-tags').val() || []
      },
      speed_optimization: $('#mt_primelink-export-speed-optimization').is(':checked'),
      iterative_processing: $('#mt_primelink-export-iterative-processing').is(':checked'),
      iteration_size: parseInt($('#mt_primelink-export-iteration-size').val() || '20', 10),
      incremental: {
        enabled: $('#mt_primelink-export-incremental-enabled').is(':checked'),
        since: $('#mt_primelink-export-incremental-since').val() || '',
        skip_unchanged: $('#mt_primelink-export-incremental-skip-unchanged').is(':checked')
      },
      test_mode: {
        enabled: $('#mt_primelink-export-test-mode').is(':checked'),
        limit: parseInt($('#mt_primelink-export-test-limit').val() || '10', 10)
      },
      notifications: {
        override: $('#mt_primelink-export-notify-override').is(':checked'),
        email: $('#mt_primelink-export-notify-email-override').val() || '',
        finish_template: $('#mt_primelink-export-notify-finish-override').val() || '',
        error_template: $('#mt_primelink-export-notify-error-override').val() || '',
        attach_file: $('#mt_primelink-export-email-attachment').is(':checked')
      },
      schedule: schedule
    };
    return data;
  }

  async function saveExportStep3(){
    const exportId = currentExportId() || parseInt($('.mt_primelink-export-step-3').data('export-id'), 10) || 0;
    if(!exportId){
      try{
        const draft = await restPost('/export/create-draft', {});
        exportId = draft.export_id || 0;
        if(exportId){
          setExportIdEverywhere(exportId);
          $('.mt_primelink-export-step-3').attr('data-export-id', exportId);
        }
      }catch(e){
        debugConsole.error('Export ID missing:', e);
        return;
      }
    }
    const data = collectExportStep3State();
    try{
      await restPost('/export/save-step', {
        export_id: exportId,
        step: 3,
        data: data
      });
    }catch(e){
      debugConsole.error('Failed to save export step 3:', e);
    }
  }

  function applyExportStep3State(data){
    data = data || {};
    
    // Format options
    if(data.format) $('#mt_primelink-export-format').val(data.format);
    if(data.csv_delimiter) $('#mt_primelink-csv-delimiter').val(data.csv_delimiter);
    if(data.csv_encoding) $('#mt_primelink-csv-encoding').val(data.csv_encoding);
    if(data.include_headers !== undefined) $('#mt_primelink-export-include-headers').prop('checked', data.include_headers);
    if(data.limit !== undefined) $('#mt_primelink-export-limit').val(data.limit);
    
    // Filters
    if(data.filters){
      if(data.filters.status) $('#mt_primelink-export-status').val(data.filters.status);
      if(Array.isArray(data.filters.categories)) $('#mt_primelink-export-categories').val(data.filters.categories);
      if(Array.isArray(data.filters.tags)) $('#mt_primelink-export-tags').val(data.filters.tags);
    }
    
    // Advanced options
    if(data.speed_optimization !== undefined) $('#mt_primelink-export-speed-optimization').prop('checked', data.speed_optimization);
    if(data.iterative_processing !== undefined) $('#mt_primelink-export-iterative-processing').prop('checked', data.iterative_processing);
    if(data.iteration_size) $('#mt_primelink-export-iteration-size').val(data.iteration_size);
    
    // Incremental export
    if(data.incremental){
      if(data.incremental.enabled !== undefined){
        $('#mt_primelink-export-incremental-enabled').prop('checked', data.incremental.enabled);
        if(data.incremental.enabled){
          $('#mt_primelink-export-incremental-options').slideDown(120);
        }
      }
      if(data.incremental.since) $('#mt_primelink-export-incremental-since').val(data.incremental.since);
      if(data.incremental.skip_unchanged !== undefined) $('#mt_primelink-export-incremental-skip-unchanged').prop('checked', data.incremental.skip_unchanged);
    }
    
    // Test mode
    if(data.test_mode){
      if(data.test_mode.enabled !== undefined){
        $('#mt_primelink-export-test-mode').prop('checked', data.test_mode.enabled);
        if(data.test_mode.enabled){
          $('#mt_primelink-export-test-mode-options').slideDown(120);
        }
      }
      if(data.test_mode.limit) $('#mt_primelink-export-test-limit').val(data.test_mode.limit);
    }
    
    // Notifications
    if(data.notifications){
      if(data.notifications.override !== undefined){
        $('#mt_primelink-export-notify-override').prop('checked', data.notifications.override);
        if(data.notifications.override){
          $('#mt_primelink-export-notify-override-wrap').slideDown(120);
        }
      }
      if(data.notifications.email) $('#mt_primelink-export-notify-email-override').val(data.notifications.email);
      if(data.notifications.finish_template) $('#mt_primelink-export-notify-finish-override').val(data.notifications.finish_template);
      if(data.notifications.error_template) $('#mt_primelink-export-notify-error-override').val(data.notifications.error_template);
      if(data.notifications.attach_file !== undefined) $('#mt_primelink-export-email-attachment').prop('checked', data.notifications.attach_file);
    }
    
    // Schedule
    if(data.schedule){
      if(data.schedule.enabled !== undefined){
        $('#mt_primelink-export-enable-schedule').prop('checked', data.schedule.enabled);
        if(data.schedule.enabled){
          $('#mt_primelink-export-schedule-wrap').slideDown(120);
        }
      }
      if(data.schedule.name) $('#mt_primelink-export-schedule-name').val(data.schedule.name);
      if(data.schedule.type) $('#mt_primelink-export-schedule-type').val(data.schedule.type);
      if(data.schedule.interval) $('#mt_primelink-export-schedule-interval').val(data.schedule.interval);
      if(data.schedule.start_time) $('#mt_primelink-export-schedule-start').val(data.schedule.start_time);
      if(data.schedule.cron) $('#mt_primelink-export-schedule-cron').val(data.schedule.cron);
      if(data.schedule.timezone) $('#mt_primelink-export-schedule-timezone').val(data.schedule.timezone);
      if(data.schedule.start_date) $('#mt_primelink-export-schedule-start-date').val(data.schedule.start_date);
      if(data.schedule.end_date) $('#mt_primelink-export-schedule-end-date').val(data.schedule.end_date);
      if(data.schedule.max_runs) $('#mt_primelink-export-schedule-max-runs').val(data.schedule.max_runs);
      if(data.schedule.retry_failure !== undefined){
        $('#mt_primelink-export-schedule-retry-failure').prop('checked', data.schedule.retry_failure);
        if(data.schedule.retry_failure){
          $('#mt_primelink-export-schedule-retry-options').slideDown(120);
        }
      }
      if(data.schedule.retry_attempts) $('#mt_primelink-export-schedule-retry-attempts').val(data.schedule.retry_attempts);
      if(data.schedule.retry_delay) $('#mt_primelink-export-schedule-retry-delay').val(data.schedule.retry_delay);
      if(data.schedule.pause_on_error !== undefined) $('#mt_primelink-export-schedule-pause-on-error').prop('checked', data.schedule.pause_on_error);
      if(data.schedule.month_days) $('#mt_primelink-export-schedule-month-days').val(data.schedule.month_days);
      if(Array.isArray(data.schedule.days)){
        $('.mt_primelink-export-schedule-day').prop('checked', false);
        data.schedule.days.forEach(day => {
          $('.mt_primelink-export-schedule-day[value="' + day + '"]').prop('checked', true);
        });
      }
      if(Array.isArray(data.schedule.times) && data.schedule.times.length > 0){
        const $wrap = $('#mt_primelink-export-schedule-times-wrap');
        $wrap.empty();
        data.schedule.times.forEach((time, idx) => {
          const $row = $(renderExportScheduleTimeRow(time, idx > 0));
          $wrap.append($row);
        });
      }
      if(data.schedule.weekday_filter) $('#mt_primelink-export-schedule-weekday-filter').val(data.schedule.weekday_filter);
      
      // Update UI based on schedule type
      updateExportScheduleTypeUI();
    }
  }

  function renderExportScheduleTimeRow(time, showRemove){
    return '<div class="mt_primelink-export-schedule-time-row" style="display:grid;grid-template-columns:1fr auto;gap:8px;margin-bottom:4px;">' +
      '<input type="time" class="mt_primelink-export-schedule-time-slot" value="' + escapeHtml(time || '00:00') + '" />' +
      '<button type="button" class="button mt_primelink-export-schedule-time-remove" ' + (showRemove ? '' : 'style="display:none;"') + '>Remove</button>' +
      '</div>';
  }

  function updateExportScheduleTypeUI(){
    const type = $('#mt_primelink-export-schedule-type').val();
    $('.mt_primelink-export-schedule-type-wrap').hide();
    
    if(type === 'preset'){
      $('#mt_primelink-export-schedule-preset-wrap').show();
      const interval = $('#mt_primelink-export-schedule-interval').val();
      if(interval === 'weekly'){
        $('#mt_primelink-export-schedule-weekly-options').show();
      } else {
        $('#mt_primelink-export-schedule-weekly-options').hide();
      }
      if(interval === 'monthly'){
        $('#mt_primelink-export-schedule-monthly-options').show();
      } else {
        $('#mt_primelink-export-schedule-monthly-options').hide();
      }
    } else if(type === 'custom'){
      $('#mt_primelink-export-schedule-custom-wrap').show();
    } else if(type === 'advanced'){
      $('#mt_primelink-export-schedule-advanced-wrap').show();
    }
  }

  // Toggle incremental export options
  $(document).on('change', '#mt_primelink-export-incremental-enabled', function(){
    if($(this).is(':checked')){
      $('#mt_primelink-export-incremental-options').slideDown(120);
    } else {
      $('#mt_primelink-export-incremental-options').slideUp(120);
    }
    saveExportStep3();
  });

  // Toggle test mode options
  $(document).on('change', '#mt_primelink-export-test-mode', function(){
    if($(this).is(':checked')){
      $('#mt_primelink-export-test-mode-options').slideDown(120);
    } else {
      $('#mt_primelink-export-test-mode-options').slideUp(120);
    }
    saveExportStep3();
  });

  // Toggle notifications override
  $(document).on('change', '#mt_primelink-export-notify-override', function(){
    if($(this).is(':checked')){
      $('#mt_primelink-export-notify-override-wrap').slideDown(120);
    } else {
      $('#mt_primelink-export-notify-override-wrap').slideUp(120);
    }
    saveExportStep3();
  });

  // Test export button
  $(document).on('click', '#mt_primelink-run-test-export', async function(){
    const exportId = currentExportId() || parseInt($('.mt_primelink-export-step-3').data('export-id'), 10) || 0;
    if(!exportId){
      alert('Please complete step 1 first.');
      return;
    }
    
    const $btn = $(this);
    const $results = $('#mt_primelink-export-test-results');
    const $resultsContent = $('#mt_primelink-export-test-results-content');
    
    $btn.prop('disabled', true).text('Running test...');
    $results.hide();
    $resultsContent.html('<p>Running test export...</p>');
    
    try{
      const testLimit = parseInt($('#mt_primelink-export-test-limit').val() || '10', 10);
      const state = collectExportStep3State();
      
      // Run test export (similar to preview but with limit)
      const res = await restPost('/export/preview', {
        export_id: exportId,
        page: 1,
        per_page: testLimit
      });
      
      const total = res.total || 0;
      const rows = res.rows || [];
      
      let html = '<div class="mt_primelink-test-results-summary">';
      html += '<p><strong>Test Results:</strong></p>';
      html += '<ul>';
      html += '<li>Total products matching filters: ' + total + '</li>';
      html += '<li>Products in test preview: ' + rows.length + '</li>';
      html += '<li>Export format: ' + escapeHtml(state.format || 'csv') + '</li>';
      html += '<li>Include headers: ' + (state.include_headers ? 'Yes' : 'No') + '</li>';
      html += '</ul>';
      html += '</div>';
      
      if(rows.length > 0){
        html += '<p style="margin-top:12px;"><strong>Preview of first ' + rows.length + ' product(s):</strong></p>';
        html += '<div style="max-height:300px;overflow-y:auto;margin-top:8px;">';
        html += '<table style="width:100%;font-size:12px;">';
        html += '<thead><tr>';
        const headers = res.headers || [];
        headers.forEach(h => {
          html += '<th style="padding:4px;border:1px solid rgba(255,255,255,0.1);">' + escapeHtml(h) + '</th>';
        });
        html += '</tr></thead><tbody>';
        rows.forEach(row => {
          html += '<tr>';
          row.forEach(cell => {
            html += '<td style="padding:4px;border:1px solid rgba(255,255,255,0.1);">' + escapeHtml(String(cell || '').substring(0, 50)) + '</td>';
          });
          html += '</tr>';
        });
        html += '</tbody></table>';
        html += '</div>';
      }
      
      $resultsContent.html(html);
      $results.slideDown(200);
    }catch(e){
      $resultsContent.html('<p style="color:#f00;">Test failed: ' + escapeHtml(e.json?.message || 'Unknown error') + '</p>');
      $results.slideDown(200);
    }finally{
      $btn.prop('disabled', false).text('Run Test Export');
    }
  });

  // Toggle schedule options
  $(document).on('change', '#mt_primelink-export-enable-schedule', function(){
    if($(this).is(':checked')){
      $('#mt_primelink-export-schedule-wrap').slideDown(120);
    } else {
      $('#mt_primelink-export-schedule-wrap').slideUp(120);
    }
    saveExportStep3();
  });

  // Schedule type change
  $(document).on('change', '#mt_primelink-export-schedule-type', function(){
    updateExportScheduleTypeUI();
    saveExportStep3();
  });

  // Schedule interval change
  $(document).on('change', '#mt_primelink-export-schedule-interval', function(){
    const interval = $(this).val();
    if(interval === 'weekly'){
      $('#mt_primelink-export-schedule-weekly-options').slideDown(120);
      $('#mt_primelink-export-schedule-monthly-options').slideUp(120);
    } else if(interval === 'monthly'){
      $('#mt_primelink-export-schedule-monthly-options').slideDown(120);
      $('#mt_primelink-export-schedule-weekly-options').slideUp(120);
    } else {
      $('#mt_primelink-export-schedule-weekly-options').slideUp(120);
      $('#mt_primelink-export-schedule-monthly-options').slideUp(120);
    }
    saveExportStep3();
  });

  // Add time slot
  $(document).on('click', '#mt_primelink-export-schedule-time-add', function(){
    const $wrap = $('#mt_primelink-export-schedule-times-wrap');
    const $rows = $wrap.find('.mt_primelink-export-schedule-time-row');
    const $row = $(renderExportScheduleTimeRow('00:00', $rows.length > 0));
    $wrap.append($row);
    $wrap.find('.mt_primelink-export-schedule-time-remove').show();
    saveExportStep3();
  });

  // Remove time slot
  $(document).on('click', '.mt_primelink-export-schedule-time-remove', function(){
    $(this).closest('.mt_primelink-export-schedule-time-row').remove();
    const $rows = $('#mt_primelink-export-schedule-times-wrap').find('.mt_primelink-export-schedule-time-row');
    if($rows.length <= 1){
      $rows.find('.mt_primelink-export-schedule-time-remove').hide();
    }
    saveExportStep3();
  });

  // Retry on failure toggle
  $(document).on('change', '#mt_primelink-export-schedule-retry-failure', function(){
    if($(this).is(':checked')){
      $('#mt_primelink-export-schedule-retry-options').slideDown(120);
    } else {
      $('#mt_primelink-export-schedule-retry-options').slideUp(120);
    }
    saveExportStep3();
  });

  // Auto-save for all step 3 inputs
  $(document).on('change blur', '.mt_primelink-export-step-3 input, .mt_primelink-export-step-3 select, .mt_primelink-export-step-3 textarea', function(){
    saveExportStep3();
  });

  // Load saved step 3 data on page load
  $(document).ready(function(){
    if($('.mt_primelink-export-step-3').length){
      const exportId = currentExportId() || parseInt($('.mt_primelink-export-step-3').data('export-id'), 10) || 0;
      if(exportId){
        restPost('/export/get-draft', { export_id: exportId }).then(draft => {
          if(draft && draft.step_data && draft.step_data[3]){
            applyExportStep3State(draft.step_data[3]);
          }
        }).catch(e => {
          debugConsole.error('Failed to load export step 3:', e);
        });
      }
    }
  });

  // Anomalies Modal Functions
  async function showAnomaliesModal(importId) {
    const $modal = $('#mt-primelink-anomalies-modal');
    if ($modal.length === 0) return;
    
    // Store import ID in modal for later use
    $modal.data('import-id', importId);
    
    $modal.show();
    $('#mt-primelink-anomalies-loading').show();
    $('#mt-primelink-anomalies-content').hide();
    $('#mt-primelink-anomalies-empty').hide();
    
    try {
      const response = await restPost('/import/anomalies', { import_id: importId });
      const anomalies = response.anomalies || [];
      
      $('#mt-primelink-anomalies-loading').hide();
      
      if (anomalies.length === 0) {
        $('#mt-primelink-anomalies-empty').show();
        $('.mt-primelink-anomalies-export-wrapper').hide();
        return;
      }
      
      $('#mt-primelink-anomalies-count').text(anomalies.length);
      $('#mt-primelink-anomalies-content').show();
      
      // Show export button
      $('.mt-primelink-anomalies-export-wrapper').show();
      
      // Store anomalies data for export
      $modal.data('anomalies-data', anomalies);
      
      // Render anomalies list as a compact table
      const $list = $('#mt-primelink-anomalies-list');
      $list.empty();

      const $table = $('<table class="mt-primelink-anomalies-table"></table>');
      const $thead = $('<thead><tr>' +
        '<th>Row</th>' +
        '<th>Item</th>' +
        '<th>Level</th>' +
        '<th>Type</th>' +
        '<th>Message</th>' +
      '</tr></thead>');
      const $tbody = $('<tbody></tbody>');

      anomalies.forEach((anomalyGroup, index) => {
        const itemData = anomalyGroup.item_data || {};
        const anomaliesList = anomalyGroup.anomalies || [];
        const itemIndex = anomalyGroup.item_index !== undefined ? anomalyGroup.item_index : index;
        const rowNumber = itemIndex + 1;

        // Extract title from various possible structures (flat and nested)
        let title = '';
        if (itemData.post && itemData.post.post_title) {
          title = itemData.post.post_title;
        } else if (itemData.post_title) {
          title = itemData.post_title;
        } else if (itemData.product && itemData.product.name) {
          title = itemData.product.name;
        } else if (itemData.name) {
          title = itemData.name;
        } else if (itemData.product && itemData.product.sku) {
          title = itemData.product.sku;
        } else if (itemData.sku) {
          title = itemData.sku;
        } else {
          title = 'Unknown Item';
        }

        anomaliesList.forEach(anomaly => {
          const level = (anomaly.level || 'info').toLowerCase();
          const type  = anomaly.type || 'Unknown';
          const msg   = anomaly.message || 'No message';

          const $tr = $('<tr></tr>');
          $tr.append(`<td class="mt-primelink-anomaly-col-row">${rowNumber}</td>`);
          
          // Make title clickable to navigate to the item
          const $itemCell = $('<td class="mt-primelink-anomaly-col-item"></td>');
          const $itemLink = $('<a href="#" class="mt-primelink-anomaly-item-link" data-item-index="' + itemIndex + '" data-import-id="' + importId + '"></a>');
          $itemLink.text(title);
          $itemCell.append($itemLink);
          $tr.append($itemCell);

          const $levelTd = $('<td class="mt-primelink-anomaly-col-level"></td>');
          const $badge   = $('<span class="mt-primelink-anomaly-badge"></span>');
          $badge.addClass(level);
          $badge.text(level.toUpperCase());
          $levelTd.append($badge);
          $tr.append($levelTd);

          $tr.append(`<td class="mt-primelink-anomaly-col-type">${type}</td>`);
          $tr.append(`<td class="mt-primelink-anomaly-col-message">${msg}</td>`);

          $tbody.append($tr);
        });
      });

      $table.append($thead);
      $table.append($tbody);
      $list.append($table);
    } catch (e) {
      $('#mt-primelink-anomalies-loading').hide();
      $('#mt-primelink-anomalies-content').hide();
      $('#mt-primelink-anomalies-empty').show();
      $('#mt-primelink-anomalies-empty p').text('Failed to load anomalies: ' + (e.message || 'Unknown error'));
    }
  }
  
  function hideAnomaliesModal() {
    $('#mt-primelink-anomalies-modal').hide();
  }
  
  // Close modal handlers
  $(document).on('click', '#mt-primelink-anomalies-modal-close, #mt-primelink-anomalies-modal-close-btn', function(e) {
    e.preventDefault();
    hideAnomaliesModal();
  });
  
  // Handle click on anomaly item link to navigate to item in import
  $(document).on('click', '.mt-primelink-anomaly-item-link', function(e) {
    e.preventDefault();
    const itemIndex = parseInt($(this).data('item-index'), 10);
    const importId = parseInt($(this).data('import-id'), 10);
    
    if (isNaN(itemIndex) || isNaN(importId) || importId < 1) {
      return;
    }
    
    // Get import type from the import row on the page
    const $importRow = $('.mt_primelink-import-row[data-id="' + importId + '"]');
    let importType = $importRow.data('type') || '';
    
    // Normalize import type for URL (convert singular to plural)
    const typeMap = {
      'product': 'products',
      'order': 'orders',
      'customer': 'customers',
      'coupon': 'coupons',
      'category': 'categories',
      'tag': 'tags',
      'attribute': 'attributes',
      'variation': 'variations',
      'review': 'reviews',
      'download': 'downloads'
    };
    if (typeMap[importType]) {
      importType = typeMap[importType];
    }
    
    // If import type not found, try to get it from draft
    if (!importType) {
      restPost('/import/get', { import_id: importId }).then(function(response) {
        if (response && response.draft) {
          const draftType = response.draft.type || (response.draft.step_data && response.draft.step_data[1] && response.draft.step_data[1].type) || '';
          const normalizedType = typeMap[draftType] || draftType;
          navigateToImportItem(importId, normalizedType, itemIndex);
        }
      }).catch(function() {
        // Fallback: navigate without type
        navigateToImportItem(importId, '', itemIndex);
      });
    } else {
      navigateToImportItem(importId, importType, itemIndex);
    }
  });
  
  function navigateToImportItem(importId, importType, itemIndex) {
    const adminUrl = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) ? mtPrimeLink.adminUrl : '';
    let url = adminUrl + 'admin.php?page=mt-primelink-new-import&import_id=' + importId + '&step=4';
    
    if (importType) {
      url += '&import_type=' + encodeURIComponent(importType);
    }
    
    // Add preview_index parameter to navigate to specific row
    url += '&preview_index=' + itemIndex;
    
    // Close modal and navigate
    hideAnomaliesModal();
    window.location.href = url;
  }
  
  // Export dropdown toggle
  $(document).on('click', '.mt-primelink-export-button', function(e) {
    e.stopPropagation();
    const $dropdown = $(this).closest('.mt-primelink-dropdown');
    $dropdown.toggleClass('open');
  });
  
  // Close dropdown when clicking outside
  $(document).on('click', function(e) {
    if (!$(e.target).closest('.mt-primelink-dropdown').length) {
      $('.mt-primelink-dropdown').removeClass('open');
    }
  });
  
  // Export to CSV
  $(document).on('click', '.mt-primelink-export-csv', function(e) {
    e.preventDefault();
    e.stopPropagation();
    const $modal = $('#mt-primelink-anomalies-modal');
    const anomalies = $modal.data('anomalies-data') || [];
    const importId = $modal.data('import-id') || 0;
    exportAnomaliesToCSV(anomalies, importId);
    $('.mt-primelink-dropdown').removeClass('open');
  });
  
  // Export to Excel
  $(document).on('click', '.mt-primelink-export-excel', function(e) {
    e.preventDefault();
    e.stopPropagation();
    const $modal = $('#mt-primelink-anomalies-modal');
    const anomalies = $modal.data('anomalies-data') || [];
    const importId = $modal.data('import-id') || 0;
    exportAnomaliesToExcel(anomalies, importId);
    $('.mt-primelink-dropdown').removeClass('open');
  });
  
  // Export anomalies to CSV
  function exportAnomaliesToCSV(anomalies, importId) {
    const headers = ['Row', 'Item', 'Level', 'Type', 'Message'];
    const rows = [];
    
    anomalies.forEach((anomalyGroup, index) => {
      const itemData = anomalyGroup.item_data || {};
      const anomaliesList = anomalyGroup.anomalies || [];
      const itemIndex = anomalyGroup.item_index !== undefined ? anomalyGroup.item_index : index;
      const rowNumber = itemIndex + 1;
      
      // Extract title
      let title = '';
      if (itemData.post && itemData.post.post_title) {
        title = itemData.post.post_title;
      } else if (itemData.post_title) {
        title = itemData.post_title;
      } else if (itemData.product && itemData.product.name) {
        title = itemData.product.name;
      } else if (itemData.name) {
        title = itemData.name;
      } else if (itemData.product && itemData.product.sku) {
        title = itemData.product.sku;
      } else if (itemData.sku) {
        title = itemData.sku;
      } else {
        title = 'Unknown Item';
      }
      
      anomaliesList.forEach(anomaly => {
        const level = (anomaly.level || 'info').toUpperCase();
        const type = anomaly.type || 'Unknown';
        const msg = anomaly.message || 'No message';
        rows.push([rowNumber, title, level, type, msg]);
      });
    });
    
    // Convert to CSV
    let csv = headers.map(h => escapeCSVField(h)).join(',') + '\n';
    rows.forEach(row => {
      csv += row.map(field => escapeCSVField(field)).join(',') + '\n';
    });
    
    // Create download link
    const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement('a');
    const url = URL.createObjectURL(blob);
    link.setAttribute('href', url);
    link.setAttribute('download', 'anomalies-import-' + importId + '-' + new Date().toISOString().split('T')[0] + '.csv');
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }
  
  // Export anomalies to Excel (XLSX) - using CSV format (Excel can open CSV files)
  function exportAnomaliesToExcel(anomalies, importId) {
    // Excel can open CSV files, so we'll export as CSV with .xlsx extension
    // For a true Excel format with formatting, you would need a library like SheetJS
    const headers = ['Row', 'Item', 'Level', 'Type', 'Message'];
    const rows = [];
    
    anomalies.forEach((anomalyGroup, index) => {
      const itemData = anomalyGroup.item_data || {};
      const anomaliesList = anomalyGroup.anomalies || [];
      const itemIndex = anomalyGroup.item_index !== undefined ? anomalyGroup.item_index : index;
      const rowNumber = itemIndex + 1;
      
      // Extract title
      let title = '';
      if (itemData.post && itemData.post.post_title) {
        title = itemData.post.post_title;
      } else if (itemData.post_title) {
        title = itemData.post_title;
      } else if (itemData.product && itemData.product.name) {
        title = itemData.product.name;
      } else if (itemData.name) {
        title = itemData.name;
      } else if (itemData.product && itemData.product.sku) {
        title = itemData.product.sku;
      } else if (itemData.sku) {
        title = itemData.sku;
      } else {
        title = 'Unknown Item';
      }
      
      anomaliesList.forEach(anomaly => {
        const level = (anomaly.level || 'info').toUpperCase();
        const type = anomaly.type || 'Unknown';
        const msg = anomaly.message || 'No message';
        rows.push([rowNumber, title, level, type, msg]);
      });
    });
    
    // Convert to CSV with comma separator (Excel can open CSV files with .xlsx extension)
    let csv = headers.map(h => escapeCSVField(h, ',')).join(',') + '\n';
    rows.forEach(row => {
      csv += row.map(field => escapeCSVField(field, ',')).join(',') + '\n';
    });
    
    // Create download link with .xlsx extension
    // Excel will open this CSV file correctly
    const blob = new Blob(['\ufeff' + csv], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;' });
    const link = document.createElement('a');
    const url = URL.createObjectURL(blob);
    link.setAttribute('href', url);
    link.setAttribute('download', 'anomalies-import-' + importId + '-' + new Date().toISOString().split('T')[0] + '.xlsx');
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }
  
  // Escape CSV field
  function escapeCSVField(field, separator = ',') {
    if (field === null || field === undefined) {
      return '';
    }
    const str = String(field);
    // If field contains separator, quote, or newline, wrap in quotes and escape quotes
    if (str.includes(separator) || str.includes('"') || str.includes('\n')) {
      return '"' + str.replace(/"/g, '""') + '"';
    }
    return str;
  }
  
  // Close on overlay click
  $(document).on('click', '#mt-primelink-anomalies-modal', function(e) {
    if ($(e.target).is('#mt-primelink-anomalies-modal')) {
      hideAnomaliesModal();
    }
  });
  
  // Close on ESC key
  $(document).on('keydown', function(e) {
    if (e.key === 'Escape' && $('#mt-primelink-anomalies-modal').is(':visible')) {
      hideAnomaliesModal();
    }
  });
  
  // Check for anomalies when manage-imports page loads for finished imports
  $(document).ready(function() {
    if ($('.mt_primelink-import-row').length > 0) {
      $('.mt_primelink-import-row').each(function() {
        const $row = $(this);
        const importId = parseInt($row.data('id'), 10);
        if (!importId) {
          return;
        }
        // Întotdeauna verificăm dacă există anomalii pentru acest import
        restPost('/import/anomalies', { import_id: importId }).then(response => {
          const anomalies = response.anomalies || [];
          ensureAnomalyButton(importId, anomalies.length);
        }).catch(() => {
          // Ignore errors
        });
      });
    }
  });
  
  // Handler for review anomalies button
  $(document).on('click', '.mt-primelink-review-anomalies', function() {
    const importId = parseInt($(this).data('id'), 10);
    if (importId > 0) {
      showAnomaliesModal(importId);
    }
  });

  // Check license status on page load
  $(document).ready(function() {
    if ($('#mt_primelink-license-status').length) {
      const $status = $('#mt_primelink-license-status');
      const activation = window.mt_primelinkSettings?.license_activation || {};
      const isActive = activation.activated || false;
      
      if (isActive) {
        $status.text('Active').removeClass('mt_primelink-status-idle').addClass('mt_primelink-status-finished');
      } else {
        $status.text('Inactive').removeClass('mt_primelink-status-finished').addClass('mt_primelink-status-idle');
      }
    }
  });

  // License test & activate handler
  $(document).on('click', '#mt-primelink-test-license', function(e) {
    e.preventDefault();
    
    const $btn = $(this);
    const $result = $('#mt-primelink-license-test-result');
    const $status = $('#mt_primelink-license-status');
    const purchaseCode = $('#mt_primelink-license-key').val().trim();
    const clientName = $('#mt_primelink-license-client-name').val().trim();
    
    const originalText = $btn.text();
    $btn.prop('disabled', true).text('Activating...');
    $result.hide();
    $status.text('Activating...').removeClass('mt_primelink-status-idle mt_primelink-status-finished').addClass('mt_primelink-status-idle');
    
    $.ajax({
      url: mtPrimeLink.restUrl + '/settings/test-license',
      method: 'POST',
      headers: {
        'X-WP-Nonce': mtPrimeLink.restNonce
      },
      data: {
        purchase_code: purchaseCode,
        client_name: clientName
      },
      success: function(response) {
        if (response.success) {
          $result.removeClass('error').addClass('success')
            .html('<strong>' + escapeHtml('Success:') + '</strong> ' + escapeHtml(response.message || 'License activated successfully!'))
            .fadeIn();
          $status.text('Active').removeClass('mt_primelink-status-idle').addClass('mt_primelink-status-finished');
          
          // Reload page after 1.5 seconds to show updated license status
          setTimeout(function() {
            window.location.reload();
          }, 1500);
        } else {
          $result.removeClass('success').addClass('error')
            .html('<strong>' + escapeHtml('Error:') + '</strong> ' + escapeHtml(response.error || 'License activation failed.'))
            .fadeIn();
          $status.text('Failed').removeClass('mt_primelink-status-finished').addClass('mt_primelink-status-idle');
          $btn.prop('disabled', false).text(originalText);
        }
      },
      error: function(xhr) {
        const error = xhr.responseJSON?.error || xhr.responseJSON?.message || 'Failed to activate license. Please try again.';
        $result.removeClass('success').addClass('error')
          .html('<strong>' + escapeHtml('Error:') + '</strong> ' + escapeHtml(error))
          .fadeIn();
        $status.text('Failed').removeClass('mt_primelink-status-finished').addClass('mt_primelink-status-idle');
        $btn.prop('disabled', false).text(originalText);
      }
    });
  });

  // License check updates handler
  $(document).on('click', '#mt-primelink-check-updates', function(e) {
    e.preventDefault();
    
    const $btn = $(this);
    const $result = $('#mt-primelink-license-test-result');
    
    const originalText = $btn.text();
    $btn.prop('disabled', true).text('Checking...');
    $result.hide();
    
    $.ajax({
      url: mtPrimeLink.restUrl + '/settings/check-updates',
      method: 'GET',
      headers: {
        'X-WP-Nonce': mtPrimeLink.restNonce
      },
      success: function(response) {
        if (response.success) {
          let message = '<strong>Update Check Results:</strong><br>';
          message += 'Current Version: ' + escapeHtml(response.current_version) + '<br>';
          
          if (response.has_update) {
            message += '<span style="color:#28a745;">✓ New version available: ' + escapeHtml(response.update_data.version || 'N/A') + '</span><br>';
            if (response.update_data.download_url) {
              message += 'Download URL: ' + escapeHtml(response.update_data.download_url) + '<br>';
            }
            if (response.update_data.changelog) {
              message += 'Changelog: ' + escapeHtml(response.update_data.changelog.substring(0, 200)) + '...<br>';
            }
          } else {
            message += '<span style="color:#6c757d;">No updates available</span><br>';
          }
          
          message += '<br><strong>Raw Response:</strong><br><pre style="font-size:11px;max-height:200px;overflow:auto;">' + escapeHtml(JSON.stringify(response.update_data, null, 2)) + '</pre>';
          
          $result.removeClass('error').addClass('success')
            .html(message)
            .fadeIn();
        } else {
          $result.removeClass('success').addClass('error')
            .html('<strong>Error:</strong> ' + escapeHtml(response.error || 'Failed to check for updates.'))
            .fadeIn();
        }
        $btn.prop('disabled', false).text(originalText);
      },
      error: function(xhr) {
        const error = xhr.responseJSON?.error || xhr.responseJSON?.message || 'Failed to check for updates. Please try again.';
        $result.removeClass('success').addClass('error')
          .html('<strong>Error:</strong> ' + escapeHtml(error))
          .fadeIn();
        $btn.prop('disabled', false).text(originalText);
      }
    });
  });

  // License deactivate handler
  $(document).on('click', '#mt-primelink-deactivate-license', function(e) {
    e.preventDefault();
    
    if (!confirm('Are you sure you want to deactivate your license? You will need to reactivate it to receive updates.')) {
      return;
    }
    
    const $btn = $(this);
    const $result = $('#mt-primelink-license-test-result');
    const $status = $('#mt_primelink-license-status');
    
    const originalText = $btn.text();
    $btn.prop('disabled', true).text('Deactivating...');
    $result.hide();
    $status.text('Deactivating...').removeClass('mt_primelink-status-idle mt_primelink-status-finished').addClass('mt_primelink-status-idle');
    
    $.ajax({
      url: mtPrimeLink.restUrl + '/settings/deactivate-license',
      method: 'POST',
      headers: {
        'X-WP-Nonce': mtPrimeLink.restNonce
      },
      success: function(response) {
        if (response.success) {
          $result.removeClass('error').addClass('success')
            .html('<strong>' + escapeHtml('Success:') + '</strong> ' + escapeHtml(response.message || 'License deactivated successfully.'))
            .fadeIn();
          $status.text('Inactive').removeClass('mt_primelink-status-finished').addClass('mt_primelink-status-idle');
          
          // Reload page after 1.5 seconds to show updated license status
          setTimeout(function() {
            window.location.reload();
          }, 1500);
        } else {
          $result.removeClass('success').addClass('error')
            .html('<strong>' + escapeHtml('Error:') + '</strong> ' + escapeHtml(response.error || 'License deactivation failed.'))
            .fadeIn();
          $status.text('Failed').removeClass('mt_primelink-status-finished').addClass('mt_primelink-status-idle');
          $btn.prop('disabled', false).text(originalText);
        }
      },
      error: function(xhr) {
        const error = xhr.responseJSON?.error || xhr.responseJSON?.message || 'Failed to deactivate license. Please try again.';
        $result.removeClass('success').addClass('error')
          .html('<strong>' + escapeHtml('Error:') + '</strong> ' + escapeHtml(error))
          .fadeIn();
        $status.text('Failed').removeClass('mt_primelink-status-finished').addClass('mt_primelink-status-idle');
        $btn.prop('disabled', false).text(originalText);
      }
    });
  });

  // Wizard restart handler (works for all PrimeLink pages using event delegation)
  $(document).on('click', '#mt-primelink-restart-wizard', function(e) {
    e.preventDefault();
    
    const $btn = $(this);
    const originalText = $btn.html();
    $btn.prop('disabled', true).html('<span class="dashicons dashicons-update spin"></span> ' + (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.i18n ? mtPrimeLink.i18n.restarting : 'Restarting...'));
    
    if (typeof mtPrimeLink === 'undefined') {
      alert('Error: Plugin scripts not loaded. Please refresh the page.');
      $btn.prop('disabled', false).html(originalText);
      return;
    }
    
    $.ajax({
      url: mtPrimeLink.restUrl + '/wizard/restart',
      method: 'POST',
      headers: {
        'X-WP-Nonce': mtPrimeLink.restNonce
      },
      success: function(response) {
        if (response && response.success) {
          // Redirect to dashboard to show wizard
          const adminUrl = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.adminUrl) ? mtPrimeLink.adminUrl : window.location.origin + '/wp-admin/';
          const dashboardUrl = adminUrl + 'admin.php?page=mt-primelink';
          window.location.href = dashboardUrl;
        } else {
          alert(response.message || 'Failed to restart wizard.');
          $btn.prop('disabled', false).html(originalText);
        }
      },
      error: function(xhr) {
        const errorMsg = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : 'Failed to restart wizard. Please try again.';
        alert(errorMsg);
        $btn.prop('disabled', false).html(originalText);
      }
    });
  });

  // ============================================
  // Live Feed Management
  // ============================================
  
  if ($('.mt-primelink-live-feed-container').length) {
    const restUrl = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.restUrl) ? mtPrimeLink.restUrl : '/wp-json/primelink/v1';
    const restNonce = (typeof mtPrimeLink !== 'undefined' && mtPrimeLink.restNonce) ? mtPrimeLink.restNonce : '';

    // REST helper functions
    async function restGet(endpoint) {
      const response = await fetch(restUrl + endpoint, {
        method: 'GET',
        headers: {
          'X-WP-Nonce': restNonce,
          'Content-Type': 'application/json',
        },
      });
      if (!response.ok) {
        const error = await response.json().catch(() => ({ message: 'Request failed' }));
        throw { status: response.status, json: error };
      }
      return await response.json();
    }

    async function restPost(endpoint, data) {
      const response = await fetch(restUrl + endpoint, {
        method: 'POST',
        headers: {
          'X-WP-Nonce': restNonce,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
      });
      if (!response.ok) {
        const error = await response.json().catch(() => ({ message: 'Request failed' }));
        throw { status: response.status, json: error };
      }
      return await response.json();
    }

    async function restPut(endpoint, data) {
      const response = await fetch(restUrl + endpoint, {
        method: 'PUT',
        headers: {
          'X-WP-Nonce': restNonce,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
      });
      if (!response.ok) {
        const error = await response.json().catch(() => ({ message: 'Request failed' }));
        throw { status: response.status, json: error };
      }
      return await response.json();
    }

    async function restDelete(endpoint) {
      const response = await fetch(restUrl + endpoint, {
        method: 'DELETE',
        headers: {
          'X-WP-Nonce': restNonce,
          'Content-Type': 'application/json',
        },
      });
      if (!response.ok) {
        const error = await response.json().catch(() => ({ message: 'Request failed' }));
        throw { status: response.status, json: error };
      }
      return await response.json();
    }

    // Load API Keys
    async function loadApiKeys() {
      try {
        const res = await restGet('/live-feed/api-keys');
        const keys = res.keys || [];
        const $list = $('#mt-primelink-api-keys-list');
        
        if (keys.length === 0) {
          $list.html('<p class="description">' + escapeHtml('No API keys generated yet.') + '</p>');
          return;
        }

        let html = '<table class="wp-list-table widefat fixed striped"><thead><tr><th>Name</th><th>Created</th><th>Last Used</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
        keys.forEach(key => {
          const created = key.created_at ? new Date(key.created_at).toLocaleString() : 'N/A';
          const lastUsed = key.last_used ? new Date(key.last_used).toLocaleString() : 'Never';
          const statusClass = key.status === 'active' ? 'status-active' : 'status-revoked';
          html += '<tr>';
          html += '<td>' + escapeHtml(key.name) + '</td>';
          html += '<td>' + escapeHtml(created) + '</td>';
          html += '<td>' + escapeHtml(lastUsed) + '</td>';
          html += '<td><span class="' + statusClass + '">' + escapeHtml(key.status) + '</span></td>';
          html += '<td><button type="button" class="button button-small mt-primelink-revoke-key" data-key-id="' + escapeHtml(key.key_id) + '">Revoke</button></td>';
          html += '</tr>';
        });
        html += '</tbody></table>';
        $list.html(html);
      } catch (e) {
        $('#mt-primelink-api-keys-list').html('<p class="description" style="color:#dc3545;">Error loading API keys: ' + escapeHtml(e.json?.message || e.message) + '</p>');
      }
    }

    // Load Feeds
    async function loadFeeds() {
      try {
        const res = await restGet('/live-feed/feeds');
        const feeds = res.feeds || [];
        const $list = $('#mt-primelink-feeds-list');
        
        if (feeds.length === 0) {
          $list.html('<p class="description">' + escapeHtml('No feeds configured yet. Click "Add Feed" to create one.') + '</p>');
          return;
        }

        let html = '<table class="wp-list-table widefat fixed striped"><thead><tr><th>Name</th><th>Source URL</th><th>Sync Mode</th><th>Last Sync</th><th>Status</th><th>Actions</th></tr></thead><tbody>';
        feeds.forEach(feed => {
          const lastSync = feed.last_sync ? new Date(feed.last_sync).toLocaleString() : 'Never';
          const statusClass = feed.status === 'active' ? 'status-active' : 'status-inactive';
          const externalUrl = feed.external_sync_url || '';
          html += '<tr>';
          html += '<td><strong>' + escapeHtml(feed.name) + '</strong></td>';
          html += '<td>' + escapeHtml(feed.source_url) + '</td>';
          html += '<td>' + escapeHtml(feed.sync_mode) + '</td>';
          html += '<td>' + escapeHtml(lastSync) + '</td>';
          html += '<td><span class="' + statusClass + '">' + escapeHtml(feed.status) + '</span></td>';
          html += '<td>';
          html += '<button type="button" class="button button-small mt-primelink-sync-feed" data-feed-id="' + escapeHtml(feed.id) + '">Sync Now</button> ';
          html += '<button type="button" class="button button-small mt-primelink-edit-feed" data-feed-id="' + escapeHtml(feed.id) + '">Edit</button> ';
          html += '<button type="button" class="button button-small mt-primelink-delete-feed" data-feed-id="' + escapeHtml(feed.id) + '">Delete</button>';
          if (externalUrl) {
            html += '<div class="description" style="margin-top:4px;font-size:11px;word-break:break-all;">Cron URL: <code>' + escapeHtml(externalUrl) + '</code></div>';
          }
          html += '</td>';
          html += '</tr>';
        });
        html += '</tbody></table>';
        $list.html(html);
      } catch (e) {
        $('#mt-primelink-feeds-list').html('<p class="description" style="color:#dc3545;">Error loading feeds: ' + escapeHtml(e.json?.message || e.message) + '</p>');
      }
    }

    // Modal handlers
    $(document).on('click', '.mt-primelink-modal__close, .mt-primelink-modal__overlay', function(e) {
      e.preventDefault();
      e.stopPropagation();
      const $modal = $(this).closest('.mt-primelink-modal');
      if ($modal.length) {
        $modal.css('display', 'none');
        $('body').css('overflow', '');
      }
    });

    // API Key Management
    $('#mt-primelink-create-api-key').on('click', function() {
      $('#mt-primelink-api-key-modal').show();
      $('#mt-primelink-api-key-name').val('');
      $('#mt-primelink-api-key-result').hide();
    });

    $('#mt-primelink-generate-api-key').on('click', async function() {
      const name = $('#mt-primelink-api-key-name').val().trim();
      if (!name) {
        showToast('warning', 'Please enter a key name.', 'API Key');
        return;
      }

      try {
        const res = await restPost('/live-feed/api-keys', { name: name });
        $('#mt-primelink-api-key-result').show();
        $('#mt-primelink-api-key-value').text(res.api_key);
        showToast('success', 'API key generated successfully. Save it now - it won\'t be shown again!', 'API Key');
        loadApiKeys();
      } catch (e) {
        showToast('error', 'Failed to generate API key: ' + (e.json?.message || e.message), 'API Key');
      }
    });

    $(document).on('click', '.mt-primelink-revoke-key', async function() {
      if (!confirm('Are you sure you want to revoke this API key?')) return;
      const keyId = $(this).data('key-id');
      try {
        await restDelete('/live-feed/api-keys/' + keyId);
        showToast('success', 'API key revoked.', 'API Key');
        loadApiKeys();
      } catch (e) {
        showToast('error', 'Failed to revoke API key: ' + (e.json?.message || e.message), 'API Key');
      }
    });

    // Feed Management
    $('#mt-primelink-add-feed').on('click', function() {
      $('#mt-primelink-feed-modal-title').text('Add Live Feed');
      $('#mt-primelink-feed-id').val('');
      $('#mt-primelink-feed-name').val('');
      $('#mt-primelink-feed-source-type').val('prime_woocommerce');
      $('#mt-primelink-feed-source-url').val('');
      $('#mt-primelink-feed-api-key').val('');
      $('#mt-primelink-feed-wc-consumer-key').val('');
      $('#mt-primelink-feed-wc-consumer-secret').val('');
      $('#mt-primelink-feed-sync-mode').val('auto');
      $('#mt-primelink-feed-interval').val('900'); // Default: 15 minutes
      $('#mt-primelink-feed-interval-custom').val('').hide();
      $('#mt-primelink-feed-interval-wrap').show();
      // Reset auth fields visibility
      if (typeof toggleFeedAuthFields === 'function') {
        toggleFeedAuthFields('prime_woocommerce');
      }
      // Default sync fields: all checked
      $('.mt-primelink-sync-field').prop('checked', true);
      $('#mt-primelink-feed-modal').show();
    });

    $(document).on('click', '.mt-primelink-edit-feed', async function() {
      const feedId = $(this).data('feed-id');
      try {
        const feed = await restGet('/live-feed/feeds/' + feedId);
        $('#mt-primelink-feed-modal-title').text('Edit Live Feed');
        $('#mt-primelink-feed-id').val(feed.id);
        $('#mt-primelink-feed-name').val(feed.name);
        $('#mt-primelink-feed-source-type').val(feed.source_type || 'prime_woocommerce');
        $('#mt-primelink-feed-source-url').val(feed.source_url);
        $('#mt-primelink-feed-api-key').val(feed.api_key || '');
        $('#mt-primelink-feed-wc-consumer-key').val(feed.wc_consumer_key || '');
        $('#mt-primelink-feed-wc-consumer-secret').val(feed.wc_consumer_secret || '');
        toggleFeedAuthFields($('#mt-primelink-feed-source-type').val());
        $('#mt-primelink-feed-sync-mode').val(feed.sync_mode);
        // Map interval to preset or custom
        const interval = feed.interval || 900;
        const presetMap = { 300: '300', 900: '900', 1800: '1800', 3600: '3600', 21600: '21600', 86400: '86400' };
        if (presetMap[interval]) {
          $('#mt-primelink-feed-interval').val(presetMap[interval]);
          $('#mt-primelink-feed-interval-custom').hide();
        } else {
          $('#mt-primelink-feed-interval').val('custom');
          $('#mt-primelink-feed-interval-custom').val(interval).show();
        }
        $('#mt-primelink-feed-interval-wrap').toggle(feed.sync_mode === 'auto');
        // Sync fields
        const fields = feed.sync_fields || ['price','stock','description','images','categories','tags'];
        $('.mt-primelink-sync-field').prop('checked', false);
        fields.forEach(f => {
          $('.mt-primelink-sync-field[value="'+f+'"]').prop('checked', true);
        });
        $('#mt-primelink-feed-modal').show();
      } catch (e) {
        showToast('error', 'Failed to load feed: ' + (e.json?.message || e.message), 'Live Feed');
      }
    });

    $('#mt-primelink-feed-sync-mode').on('change', function() {
      $('#mt-primelink-feed-interval-wrap').toggle($(this).val() === 'auto');
    });

    // Handle interval preset vs custom
    $('#mt-primelink-feed-interval').on('change', function() {
      if ($(this).val() === 'custom') {
        $('#mt-primelink-feed-interval-custom').show().focus();
      } else {
        $('#mt-primelink-feed-interval-custom').hide();
      }
    });

    // Toggle API key vs WooCommerce REST keys depending on source type
    function toggleFeedAuthFields(sourceType) {
      if (sourceType === 'woocommerce_rest') {
        $('#mt-primelink-feed-api-key-row').hide();
        $('#mt-primelink-feed-wc-keys-row').show();
      } else {
        $('#mt-primelink-feed-api-key-row').show();
        $('#mt-primelink-feed-wc-keys-row').hide();
      }
    }

    $('#mt-primelink-feed-source-type').on('change', function() {
      toggleFeedAuthFields($(this).val());
    });

    $('#mt-primelink-save-feed').on('click', async function() {
      const feedId = $('#mt-primelink-feed-id').val();
      const sourceType = $('#mt-primelink-feed-source-type').val();
      const data = {
        name: $('#mt-primelink-feed-name').val().trim(),
        source_url: $('#mt-primelink-feed-source-url').val().trim(),
        api_key: $('#mt-primelink-feed-api-key').val().trim(),
        source_type: sourceType,
        wc_consumer_key: $('#mt-primelink-feed-wc-consumer-key').val().trim(),
        wc_consumer_secret: $('#mt-primelink-feed-wc-consumer-secret').val().trim(),
        sync_mode: $('#mt-primelink-feed-sync-mode').val(),
        interval: (function() {
          const preset = $('#mt-primelink-feed-interval').val();
          if (preset === 'custom') {
            const custom = parseInt($('#mt-primelink-feed-interval-custom').val(), 10);
            return isNaN(custom) || custom < 60 ? 300 : custom; // Fallback to 5 min if invalid
          }
          return parseInt(preset, 10);
        })(),
        status: 'active',
        sync_fields: $('.mt-primelink-sync-field:checked').map(function(){ return $(this).val(); }).get(),
      };

      // Basic validation depending on source type
      if (!data.name || !data.source_url) {
        showToast('warning', 'Please fill in at least the Name and Source URL.', 'Live Feed');
        return;
      }

      if (sourceType === 'prime_woocommerce' && !data.api_key) {
        showToast('warning', 'Please enter the PrimeLink API Key from the source store.', 'Live Feed');
        return;
      }

      if (sourceType === 'woocommerce_rest' && (!data.wc_consumer_key || !data.wc_consumer_secret)) {
        showToast('warning', 'Please enter the WooCommerce Consumer key and Consumer secret from the supplier store.', 'Live Feed');
        return;
      }

      try {
        if (feedId) {
          await restPut('/live-feed/feeds/' + feedId, data);
          showToast('success', 'Feed updated successfully.', 'Live Feed');
        } else {
          await restPost('/live-feed/feeds', data);
          showToast('success', 'Feed created successfully.', 'Live Feed');
        }
        $('#mt-primelink-feed-modal').hide();
        loadFeeds();
      } catch (e) {
        showToast('error', 'Failed to save feed: ' + (e.json?.message || e.message), 'Live Feed');
      }
    });

    $(document).on('click', '.mt-primelink-sync-feed', async function() {
      const feedId = $(this).data('feed-id');
      const $btn = $(this);
      $btn.prop('disabled', true).text('Syncing...');
      try {
        const res = await restPost('/live-feed/feeds/' + feedId + '/sync');
        showToast('success', res.message || 'Sync completed.', 'Live Feed');
        loadFeeds();
      } catch (e) {
        showToast('error', 'Sync failed: ' + (e.json?.message || e.message), 'Live Feed');
      } finally {
        $btn.prop('disabled', false).text('Sync Now');
      }
    });

    $(document).on('click', '.mt-primelink-delete-feed', async function() {
      if (!confirm('Are you sure you want to delete this feed?')) return;
      const feedId = $(this).data('feed-id');
      try {
        await restDelete('/live-feed/feeds/' + feedId);
        showToast('success', 'Feed deleted.', 'Live Feed');
        loadFeeds();
      } catch (e) {
        showToast('error', 'Failed to delete feed: ' + (e.json?.message || e.message), 'Live Feed');
      }
    });

    // Initial load
    loadApiKeys();
    loadFeeds();
  }

  // ============================================
  // Release Notes Modal
  // ============================================
  $(document).ready(function() {
    // Release Notes Modal - Open handler
    $(document).on('click', '[data-release-notes-modal]', function(e) {
      e.preventDefault();
      e.stopPropagation();
      const $modal = $('#mt-primelink-release-notes-modal');
      if ($modal.length) {
        $modal.css('display', 'flex');
        $('body').css('overflow', 'hidden');
      } else {
        debugConsole.warn('Release notes modal not found in DOM');
      }
    });

    // Release Notes Modal - Close handler (specific)
    $(document).on('click', '#mt-primelink-release-notes-modal .mt-primelink-modal__close, #mt-primelink-release-notes-modal .mt-primelink-modal__overlay', function(e) {
      e.preventDefault();
      e.stopPropagation();
      const $modal = $('#mt-primelink-release-notes-modal');
      if ($modal.length) {
        $modal.css('display', 'none');
        $('body').css('overflow', '');
      }
    });
  });

})(jQuery);