// Navigation + Logout extract (paste into your rule_configurator.js)
// Assumes these DOM elements and helper functions (renderCombinedRules, showAccountManagementView, checkAccountCodeState, loadInitialData, addVendorToSidebar, renderSummaryPage) exist elsewhere in your script.

const sidebarNav = document.getElementById('sidebar-nav');
const appPages = document.querySelectorAll('.app-page');
const summaryTableBody = document.getElementById('summary-table-body');
const vendorList = document.getElementById('vendor-list');
const logoutBtn = document.getElementById('logoutBtn');
const backToDashboardBtn = document.getElementById('backToDashboardBtn');

// --- Sidebar navigation (Summary <-> Rule Configurator) ---
if (sidebarNav) {
  sidebarNav.addEventListener('click', (e) => {
    const link = e.target.closest('.nav-link');
    if (!link) return;
    e.preventDefault();

    const targetPageId = link.dataset.page;

    // update active class on sidebar links
    sidebarNav.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
    link.classList.add('active');

    // show/hide pages
    appPages.forEach(page => {
      page.classList.toggle('hidden', page.id !== targetPageId);
    });

    // if showing summary, re-render it (function expected elsewhere)
    if (targetPageId === 'summary-page') {
      // remove the right-side selected-state header so configurator shows welcome next time
      const header = document.getElementById('selected-state-header');
      if (header) header.remove();
      if (typeof renderSummaryPage === 'function') renderSummaryPage();
      // ensure welcome view shows next time user visits configurator
      const welcomeView = document.getElementById('welcome-view');
      if (welcomeView) welcomeView.classList.remove('hidden');
    } else {
      // show welcome view by default for other pages
      if (typeof showWelcomeView === 'function') showWelcomeView();

      // If user navigates to uploader-page and we have a current state selected,
      // ensure the right-side header is visible and welcome hidden
      if (targetPageId === 'uploader-page') {
        const initials = window.activeVendorStateName || (window.activeVendorId ? String(window.activeVendorId).replace(/^state-/i, '') : '');
        if (initials) {
          if (typeof window._renderSelectedStateHeader === 'function') {
            window._renderSelectedStateHeader(initials.toUpperCase());
            const welcomeView = document.getElementById('welcome-view');
            if (welcomeView) welcomeView.classList.add('hidden');
          }
        }
      }
    }
  });
}

// --- Clicks inside summary table (open configurator for an account) ---
if (summaryTableBody) {
  summaryTableBody.addEventListener('click', (e) => {
    // handle clicking a summary row to open configurator
    const row = e.target.closest('tr[data-state-name]');
    if (row) {
      // when user clicks a summary row, open configurator and select the vendor in sidebar
      const stateName = row.dataset.stateName;
      const stateId = row.dataset.stateId || `state-${stateName}`;
      const uploaderLink = sidebarNav.querySelector('a[data-page="uploader-page"]');
      if (uploaderLink) {
        sidebarNav.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
        uploaderLink.classList.add('active');
        appPages.forEach(page => {
          page.classList.toggle('hidden', page.id !== 'uploader-page');
        });
      }

      // find sidebar entry for this state and mark selected (uses either state-id token or state-name token)
      let vendorLink = vendorList.querySelector(`a[data-vendor-id="state-${stateId}"]`) || vendorList.querySelector(`a[data-vendor-id="${stateId}"]`) || vendorList.querySelector(`a[data-state-name="${stateName}"]`) || vendorList.querySelector(`a[data-vendor-id="state-${stateName}"]`) || vendorList.querySelector(`a[data-vendor-id="${stateName}"]`);
      if (vendorLink) {
        document.querySelectorAll('#vendor-list .nav-link').forEach(nav => nav.classList.remove('bg-slate-700', 'text-white'));
        vendorLink.classList.add('bg-slate-700', 'text-white');

        // update globals
        window.activeVendorId = vendorLink.dataset.vendorId || `state-${stateName}`;
        window.activeVendorStateName = (vendorLink.dataset.stateName || stateName).toUpperCase();

        if (typeof renderCombinedRules === 'function') renderCombinedRules();
        if (typeof showAccountManagementView === 'function') showAccountManagementView(window.activeVendorId);
      }

      return;
    }
  });
}

/* --- ADDED: Make the "+" (Add State) button open the Add Vendor modal and focus the input.
   Using unique internal variable names to avoid collisions with other parts of the script.
*/
(function enableAddStatePopup() {
  const __rc_addVendorButton = document.getElementById('add-vendor-button');
  const __rc_addVendorModal = document.getElementById('add-vendor-modal');
  const __rc_vendorNameInput = document.getElementById('vendor-name-input');

  if (!__rc_addVendorButton) return;

  __rc_addVendorButton.addEventListener('click', (ev) => {
    ev.preventDefault();
    // clear any previous value
    if (__rc_vendorNameInput) __rc_vendorNameInput.value = '';
    // show modal
    if (__rc_addVendorModal) {
      __rc_addVendorModal.classList.remove('hidden');
      // focus input after a tiny delay to ensure it's visible
      setTimeout(() => {
        if (__rc_vendorNameInput) __rc_vendorNameInput.focus();
      }, 50);
    }
  });
})();

/* --- ADDED: Save new state (two-letter initials) into DB and reflect on UI.
   This only adds the minimal required logic: validation, POST to API, UI refresh.
   It will try to use API_BASE_URL if available; otherwise it calls '/api/states'.
*/
(function enableSaveState() {
  const __saveBtn = document.getElementById('save-new-vendor-button');
  const __vendorNameInput = document.getElementById('vendor-name-input');
  const __addVendorModal = document.getElementById('add-vendor-modal');

  if (!__saveBtn || !__vendorNameInput) return;

  __saveBtn.addEventListener('click', async (ev) => {
    ev.preventDefault();

    const raw = __vendorNameInput.value ? __vendorNameInput.value.trim() : '';
    const initials = raw.toUpperCase();

    // validation: exactly 2 alphabetic characters
    if (!/^[A-Z]{2}$/.test(initials)) {
      alert('Please enter state initials using exactly two letters (e.g., "KA").');
      __vendorNameInput.focus();
      return;
    }

    // determine base URL
    const __base = (typeof API_BASE_URL !== 'undefined' && API_BASE_URL) ? API_BASE_URL : '';

    try {
      const resp = await fetch(`${__base}/api/states`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ state_name: initials })
      });

      if (!resp.ok) {
        // try to extract server error message
        let errMsg = `Failed to save state (status ${resp.status}).`;
        try {
          const errJson = await resp.json();
          if (errJson && errJson.detail) errMsg = errJson.detail;
          else if (errJson && errJson.message) errMsg = errJson.message;
        } catch (e) { /* ignore parse errors */ }
        throw new Error(errMsg);
      }

      // success: refresh data and UI
      if (typeof loadInitialData === 'function') {
        await loadInitialData();
      } else if (typeof loadStates === 'function') {
        // if the loader function defined below exists, call it
        await loadStates();
      } else {
        // fallback: best-effort UI update if loader not present
        try {
          if (vendorList && !vendorList.querySelector(`a[data-vendor-id="state-${initials}"], a[data-vendor-id="${initials}"], a[data-state-name="${initials}"]`)) {
            const li = document.createElement('li');
            li.innerHTML = `<a href="#" class="nav-link w-full flex items-center space-x-3 px-3 py-2 text-sm font-medium rounded-md text-slate-300 hover:bg-slate-700 hover:text-white" data-vendor-id="state-${initials}" data-state-name="${initials}"><span class="truncate">${initials}</span></a>`;
            vendorList.appendChild(li);
          }
          if (typeof renderSummaryPage === 'function') renderSummaryPage();
          if (typeof renderCombinedRules === 'function') renderCombinedRules();
        } catch (e) {
          // ignore fallback failures
        }
      }

      // close modal and notify user
      if (__addVendorModal) __addVendorModal.classList.add('hidden');
      alert(`State "${initials}" saved successfully.`);
    } catch (err) {
      console.error('Save state failed:', err);
      alert(`Error saving state: ${err.message || err}`);
    }
  });
})();

// --- Logout (clears client-side state and redirects to login) ---
if (logoutBtn) {
  logoutBtn.addEventListener('click', async (e) => {
    e.preventDefault();
    try {
      // Attempt server-side logout (route may differ in your backend)
      await fetch('/auth/logout', { method: 'POST', credentials: 'include' });
    } catch (err) {
      console.warn('Logout request failed:', err);
    } finally {
      try { localStorage.removeItem('authToken'); } catch (e) { /* ignore */ }
      try { sessionStorage.clear(); } catch (e) { /* ignore */ }
      window.location.href = 'index.html'; // change to your login page if needed
    }
  });
}

// --- Back to VITU Dashboard button (explicit redirect) ---
if (backToDashboardBtn) {
  backToDashboardBtn.addEventListener('click', (e) => {
    e.preventDefault();
    // navigate to the dashboard page
    window.location.href = 'dashboard.html'; // adjust path if needed
  });
}

/* -------------------------
   Persisted States loader + UI sync
   ------------------------- */
(function managePersistedStates() {
  const __vendorList = document.getElementById('vendor-list');
  const __addVendorModal = document.getElementById('add-vendor-modal');
  const __vendorNameInput = document.getElementById('vendor-name-input');
  const __cancelAddVendorButton = document.getElementById('cancel-add-vendor-button');
  const __saveNewVendorButton = document.getElementById('save-new-vendor-button');
  const __summaryBody = document.getElementById('summary-table-body');

  // Build base API path (uses API_BASE_URL if defined, otherwise relative)
  const __apiBase = (typeof API_BASE_URL !== 'undefined' && API_BASE_URL) ? API_BASE_URL : '';

  // store a small in-memory cache so multiple parts of UI can read the latest list
  window._persistedStatesCache = window._persistedStatesCache || [];

  // Utility: create a sidebar entry for a state (idempotent)
  function createStateSidebarEntry(state) {
    if (!__vendorList) return;
    const idToken = state.id ? `state-${state.id}` : `state-${state.state_name}`;
    if (__vendorList.querySelector(`a[data-vendor-id="${idToken}"], a[data-state-name="${state.state_name}"]`)) return; // already present

    // If an integration helper exists, prefer it to keep behavior consistent
    if (typeof addVendorToSidebar === 'function') {
      try {
        // addVendorToSidebar expects (vendorId, displayName) in earlier code
        addVendorToSidebar(idToken, state.state_name);
        return;
      } catch (e) {
        // fallback to manual creation if helper errors
        console.warn('addVendorToSidebar helper failed, falling back to manual creation', e);
      }
    }

    const li = document.createElement('li');
    li.innerHTML = `<a href="#" class="nav-link w-full flex items-center space-x-3 px-3 py-2 text-sm font-medium rounded-md text-slate-300 hover:bg-slate-700 hover:text-white" data-vendor-id="${idToken}" data-state-name="${state.state_name}"><span class="truncate">${state.state_name}</span></a>`;
    __vendorList.appendChild(li);
  }

  // Utility: create a minimal summary-row entry for a state (idempotent)
  // NOTE: Summary table in your HTML has 2 visible columns now: State Name | Configured
  function createStateSummaryEntry(state) {
    if (!__summaryBody) return;
    const stateName = state.state_name;
    // Avoid duplicates by checking existing row dataset (we only create one <tr> per state)
    const existing = __summaryBody.querySelector(`tr[data-state-name="${stateName}"]`);
    if (existing) return;

    // Determine configured value (server can supply true/false or 1/0 or 'Y'/'N')
    let configured = false;
    if (state.configured !== undefined && state.configured !== null) {
      configured = (state.configured === true || state.configured === 1 || state.configured === '1' || String(state.configured).toLowerCase() === 'true' || String(state.configured).toUpperCase() === 'Y' || String(state.configured).toUpperCase() === 'YES');
    } else {
      // default if server not sending flag
      configured = false;
    }

    const row = document.createElement('tr');
    row.className = 'bg-white border-b hover:bg-slate-50 vendor-summary-row';
    row.dataset.vendorName = stateName;
    row.dataset.stateId = state.id || '';
    row.dataset.stateName = stateName;

    // Only two columns: State Name | Configured (Yes/No)
    row.innerHTML = `
      <td class="px-6 py-4 font-medium text-slate-900">${stateName}</td>
      <td class="px-6 py-4 text-center font-semibold ${configured ? 'text-green-700' : 'text-red-700'}">${configured ? 'Yes' : 'No'}</td>
    `;

    __summaryBody.appendChild(row);
  }

  // Load states from backend and render them
  async function loadStates() {
    try {
      const resp = await fetch(`${__apiBase}/api/states`);
      if (!resp.ok) {
        // If 404 or other, use fallback states list
        console.warn('Could not load /api/states:', resp.status, '- using fallback states');
        useFallbackStates();
        return;
      }
      const data = await resp.json();
      if (!Array.isArray(data)) {
        console.warn('Unexpected /api/states response shape', data, '- using fallback states');
        useFallbackStates();
        return;
      }

      // store cache and also try to populate appData.states if appData is present
      window._persistedStatesCache = data.map(s => ({
        id: s.id || s.state_id || null,
        state_name: (s.state_name || s.name || s.state || '').toString().toUpperCase(),
        configured: s.configured ?? s.is_configured ?? s.configured_flag ?? false
      }));

      if (typeof appData !== 'undefined') {
        appData.states = window._persistedStatesCache;
      }

      // clear existing sidebar entries created by this loader (only ones previously added by loader)
      if (__vendorList) {
        Array.from(__vendorList.querySelectorAll('a[data-vendor-id], a[data-state-name]')).forEach(a => {
          const vid = a.getAttribute('data-vendor-id') || '';
          const sname = a.getAttribute('data-state-name') || '';
          if (vid.startsWith('state-') || sname) {
            const parent = a.closest('li');
            if (parent) parent.remove();
          }
        });
      }
      // clear summary rows associated with loader
      if (__summaryBody) {
        Array.from(__summaryBody.querySelectorAll('tr[data-state-name]')).forEach(r => r.remove());
      }

      // Render each state into sidebar + summary
      window._persistedStatesCache.forEach(state => {
        if (!state.state_name) return;
        createStateSidebarEntry(state);
        createStateSummaryEntry(state);
      });

      // Attach click handlers for newly added sidebar elements
      attachVendorSidebarClickHandlers();
    } catch (err) {
      console.error('Failed to load states:', err, '- using fallback states');
      useFallbackStates();
    }
  }

  // Fallback function to provide default states when API is unavailable
  function useFallbackStates() {
    console.log('Using fallback states: PA, CT, GA');
    
    // Default states list including GA
    const fallbackStates = [
      { id: 1, state_name: 'PA', configured: false },
      { id: 2, state_name: 'CT', configured: false },
      { id: 3, state_name: 'GA', configured: false }
    ];

    window._persistedStatesCache = fallbackStates;

    if (typeof appData !== 'undefined') {
      appData.states = window._persistedStatesCache;
    }

    // clear existing sidebar entries created by this loader
    if (__vendorList) {
      Array.from(__vendorList.querySelectorAll('a[data-vendor-id], a[data-state-name]')).forEach(a => {
        const vid = a.getAttribute('data-vendor-id') || '';
        const sname = a.getAttribute('data-state-name') || '';
        if (vid.startsWith('state-') || sname) {
          const parent = a.closest('li');
          if (parent) parent.remove();
        }
      });
    }
    // clear summary rows associated with loader
    if (__summaryBody) {
      Array.from(__summaryBody.querySelectorAll('tr[data-state-name]')).forEach(r => r.remove());
    }

    // Render each fallback state into sidebar + summary
    window._persistedStatesCache.forEach(state => {
      if (!state.state_name) return;
      createStateSidebarEntry(state);
      createStateSummaryEntry(state);
    });

    // Attach click handlers for newly added sidebar elements
    attachVendorSidebarClickHandlers();
  }

  // Attach click handler to vendor list to activate selected state (idempotent)
  function attachVendorSidebarClickHandlers() {
    if (!__vendorList) return;
    // Use event delegation (safe even if called multiple times)
    __vendorList.removeEventListener('click', vendorClickHandler);
    __vendorList.addEventListener('click', vendorClickHandler);
  }

  function vendorClickHandler(e) {
    const link = e.target.closest('.nav-link');
    if (!link) return;
    e.preventDefault();

    document.querySelectorAll('#vendor-list .nav-link').forEach(nav => nav.classList.remove('bg-slate-700', 'text-white'));
    link.classList.add('bg-slate-700', 'text-white');

    // the id used here is the synthetic id we made (state-<id> or state-<NAME>)
    const vid = link.dataset.vendorId;
    activeVendorId = vid; // maintain same global var usage as your app
    window.activeVendorId = vid;

    // prefer explicit displayed state name
    const stateNameAttr = (link.dataset.stateName || '').toString().trim();
    if (stateNameAttr) {
      window.activeVendorStateName = stateNameAttr.toUpperCase();
    } else {
      window.activeVendorStateName = vid ? String(vid).replace(/^state-/i, '').toUpperCase() : '';
    }

    if (typeof renderCombinedRules === 'function') renderCombinedRules();
    if (typeof showAccountManagementView === 'function') showAccountManagementView(activeVendorId);

    // If we're on uploader page, update header and hide welcome
    const uploaderPage = document.getElementById('uploader-page');
    const welcomeView = document.getElementById('welcome-view');
    if (uploaderPage && !uploaderPage.classList.contains('hidden')) {
      if (window._renderSelectedStateHeader) {
        window._renderSelectedStateHeader(window.activeVendorStateName);
      }
      if (welcomeView) welcomeView.classList.add('hidden');
    }
  }

  // Ensure Cancel button hides the modal
  if (__cancelAddVendorButton) {
    __cancelAddVendorButton.addEventListener('click', (ev) => {
      ev.preventDefault();
      if (__addVendorModal) __addVendorModal.classList.add('hidden');
    });
  }

  // After save: refresh states (best-effort hook)
  if (__saveNewVendorButton) {
    __saveNewVendorButton.addEventListener('click', () => {
      // Wait a short moment to allow the existing save handler to complete the POST.
      setTimeout(() => {
        if (typeof loadStates === 'function') loadStates();
      }, 700);
    });
  }

  // Initial load on script append / DOM ready (call as soon as possible)
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', loadStates, { once: true });
  } else {
    setTimeout(loadStates, 0);
  }

  // expose loader for other modules
  window.loadStates = loadStates;
})();

// --- Summary search: live filter states as user types (case-insensitive) ---
(function enableSummarySearch() {
  const searchInput = document.getElementById('vendor-summary-search');
  const summaryBody = document.getElementById('summary-table-body');

  if (!searchInput || !summaryBody) return;

  searchInput.addEventListener('input', (e) => {
    const q = (e.target.value || '').trim().toLowerCase();

    // Show all if empty
    if (q === '') {
      Array.from(summaryBody.querySelectorAll('tr[data-state-name]')).forEach(row => {
        row.style.display = ''; // revert to default
      });
      return;
    }

    // Filter rows: match if state name starts with the query OR contains it anywhere.
    Array.from(summaryBody.querySelectorAll('tr[data-state-name]')).forEach(row => {
      const name = (row.dataset.stateName || '').toLowerCase();
      // Use startsWith for initial-letter behavior but also allow contains
      const matches = name.startsWith(q) || name.includes(q);
      row.style.display = matches ? '' : 'none';
    });
  });
})();

// --- Enhanced Rule Configurator Right-Side Header + Vendor Search Filtering ---
(function enableVendorSearchAndRightHeader() {
  const vendorSearchInput = document.getElementById('vendor-search-input');
  const vendorListEl = document.getElementById('vendor-list');
  const mainContent = document.getElementById('main-content');
  const welcomeView = document.getElementById('welcome-view');
  const uploadModal = document.getElementById('upload-modal');
  const showUploadModalButton = document.getElementById('show-upload-modal-button');

  if (!vendorSearchInput || !vendorListEl || !mainContent) return;

  // ✅ US state mapping (customize if needed)
  const STATE_FULL_NAMES = {
    'AL': 'Alabama','AK': 'Alaska','AZ': 'Arizona','AR': 'Arkansas','CA': 'California',
    'CO': 'Colorado','CT': 'Connecticut','DE': 'Delaware','FL': 'Florida','GA': 'Georgia',
    'HI': 'Hawaii','ID': 'Idaho','IL': 'Illinois','IN': 'Indiana','IA': 'Iowa',
    'KS': 'Kansas','KY': 'Kentucky','LA': 'Louisiana','ME': 'Maine','MD': 'Maryland',
    'MA': 'Massachusetts','MI': 'Michigan','MN': 'Minnesota','MS': 'Mississippi','MO': 'Missouri',
    'MT': 'Montana','NE': 'Nebraska','NV': 'Nevada','NH': 'New Hampshire','NJ': 'New Jersey',
    'NM': 'New Mexico','NY': 'New York','NC': 'North Carolina','ND': 'North Dakota','OH': 'Ohio',
    'OK': 'Oklahoma','OR': 'Oregon','PA': 'Pennsylvania','RI': 'Rhode Island','SC': 'South Carolina',
    'SD': 'South Dakota','TN': 'Tennessee','TX': 'Texas','UT': 'Utah','VT': 'Vermont',
    'VA': 'Virginia','WA': 'Washington','WV': 'West Virginia','WI': 'Wisconsin','WY': 'Wyoming'
  };

  // --- Search filter logic under + button ---
  vendorSearchInput.addEventListener('input', (e) => {
    const q = (e.target.value || '').trim().toLowerCase();

    Array.from(vendorListEl.querySelectorAll('a[data-state-name], a[data-vendor-id]')).forEach(a => {
      const stateName = (a.dataset.stateName || a.textContent || '').toString().toLowerCase();
      const matches = q === '' ? true : (stateName.startsWith(q) || stateName.includes(q));
      const li = a.closest('li') || a.parentElement;
      if (li) li.style.display = matches ? '' : 'none';
    });
  });

  // --- Helper: Create or update right-side state header ---
  function renderSelectedStateHeader(initials) {
    // Hide the welcome view once a state is selected
    if (welcomeView) welcomeView.classList.add('hidden');

    // Build the header element if not present
    let header = document.getElementById('selected-state-header');
    if (!header) {
      header = document.createElement('div');
      header.id = 'selected-state-header';
      // make header visually full-width and placed above the preview/table
      header.className = 'w-full block bg-white p-6 mb-4 rounded-lg shadow-md border border-slate-200';
      header.style.boxSizing = 'border-box';
    }

    const upper = (initials || '').toUpperCase();
    const full = STATE_FULL_NAMES[upper] || '';
    const displayText = full ? `${upper} [${full}]` : upper;

    header.innerHTML = `
      <div class="flex items-center justify-between w-full">
        <div class="flex items-center gap-4">
          <span class="text-2xl font-bold text-slate-800">${displayText}</span>
        </div>
        <div id="state-upload-area" class="flex items-center gap-3">
          <button id="state-upload-button" class="inline-flex items-center gap-2 bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700">
            <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
            </svg>
            <span>Upload .xlsx for ${upper}</span>
          </button>
        </div>
      </div>
    `;

    // Decide where to insert the header so it appears above the preview TABLE
    // Priority:
    // 1) Insert header as the immediate previous sibling of the preview TABLE's closest visible wrapper.
    // 2) If no preview exists, prepend to mainContent (full-width).
    const sidePreview = document.getElementById('side-excel-preview');
    const sideTable = document.getElementById('side-excel-preview-table');
    // prefer to place header ABOVE the preview container (so it is full width above the table)
    if (sidePreview && sidePreview.parentNode) {
      // If header not already inserted before sidePreview, insert it there
      if (sidePreview.previousElementSibling !== header) {
        sidePreview.parentNode.insertBefore(header, sidePreview);
      }
    } else if (sideTable && sideTable.closest('.some-wrapper')) {
      // If your layout has a wrapper around the table, try to place header before that wrapper
      const wrapper = sideTable.closest('.some-wrapper');
      if (wrapper && wrapper.parentNode) {
        if (wrapper.previousElementSibling !== header) wrapper.parentNode.insertBefore(header, wrapper);
      } else if (mainContent) {
        if (mainContent.firstElementChild !== header) mainContent.prepend(header);
      }
    } else {
      // fallback: ensure header is at top of main content area
      if (mainContent) {
        if (mainContent.firstElementChild !== header) mainContent.prepend(header);
      } else {
        // ultimate fallback: prepend to body
        if (document.body.firstElementChild !== header) document.body.prepend(header);
      }
    }

    // Ensure the preview panel is positioned below the header (helpful for some flex/grid layouts)
    // Keep it hidden by default; the actual preview renderer (renderExcelPreview)
    // will remove the 'hidden' class when it has content to show.
    if (sidePreview) {
      // do NOT unhide here — preview should appear only after parsing/upload
      sidePreview.classList.add('hidden');
      sidePreview.style.clear = 'both';
      sidePreview.style.marginTop = '0.5rem';
    }

    // If the preview container doesn't exist (we previously rendered it inside
    // the header in older versions) create it here and insert it immediately
    // after the header so renderExcelPreview can find it.
    let createdSidePreview = document.getElementById('side-excel-preview');
    if (!createdSidePreview) {
      try {
  createdSidePreview = document.createElement('div');
  createdSidePreview.id = 'side-excel-preview';
  // Use full width so preview fills the right-side area instead of a fixed 520px width
  createdSidePreview.className = 'hidden mt-3 w-full max-w-full bg-white border border-slate-100 rounded-lg p-3 shadow-md overflow-x-auto';
        createdSidePreview.innerHTML = `
          <div id="side-preview-toolbar" class="flex items-center gap-3 mb-3">
            <button id="btn-back" class="inline-flex items-center gap-2 bg-slate-100 text-slate-700 px-3 py-1 rounded-md text-sm">
              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
              Back
            </button>
            <button id="btn-import" class="inline-flex items-center gap-2 bg-slate-100 text-slate-700 px-3 py-1 rounded-md text-sm">
              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-600" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v12m0 0l4-4m-4 4l-4-4"/></svg>
              Import
            </button>
            <button id="btn-export" class="inline-flex items-center gap-2 bg-slate-100 text-slate-700 px-3 py-1 rounded-md text-sm">
              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-600" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1M12 12V3m0 0l4 4m-4-4L8 7"/></svg>
              Export
            </button>
            <button id="btn-save" class="inline-flex items-center gap-2 bg-slate-100 text-slate-700 px-3 py-1 rounded-md text-sm">
              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-emerald-600" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13v6a2 2 0 002 2h10a2 2 0 002-2v-6M5 6h14M12 3v10"/></svg>
              Save
            </button>
          </div>
          <h4 class="text-sm font-semibold text-slate-800 mb-2">Preview (first 5 rows)</h4>
          <div class="overflow-auto mb-3 max-h-[340px]">
            <table id="side-excel-preview-table" class="w-full text-sm table-auto border-collapse"></table>
          </div>
          <div id="side-excel-preview-columns" class="space-y-2">
            <h5 class="text-sm font-medium text-slate-700">Columns (3rd row)</h5>
            <div id="side-excel-preview-columns-list" class="flex flex-wrap gap-2 mt-2"></div>
          </div>
        `;
        // insert directly after header so it's visually under it
        if (header && header.parentNode) {
          header.parentNode.insertBefore(createdSidePreview, header.nextSibling);
        } else if (mainContent) {
          mainContent.appendChild(createdSidePreview);
        } else {
          document.body.appendChild(createdSidePreview);
        }
        // ensure it can expand to available space (helpful for flex/grid layouts)
        try {
          createdSidePreview.style.width = '100%';
          createdSidePreview.style.maxWidth = '100%';
          createdSidePreview.style.flex = '1 1 auto';
        } catch (e) { /* ignore */ }
        // make visible (we keep hidden until actual preview render occurs)
        createdSidePreview.classList.add('hidden');

        // Wire toolbar button actions (Back, Import, Export, Save, Upload Dataset)
        try {
          // Back: hide preview and show welcome view
          const btnBack = document.getElementById('btn-back');
          if (btnBack) btnBack.addEventListener('click', (ev) => {
            ev.preventDefault();
            try { createdSidePreview.classList.add('hidden'); } catch (e) {}
            try { const header = document.getElementById('selected-state-header'); if (header) header.remove(); } catch (e) {}
            if (welcomeView) welcomeView.classList.remove('hidden');
          });

          // Import: open file picker
          const btnImport = document.getElementById('btn-import');
          if (btnImport) btnImport.addEventListener('click', (ev) => {
            ev.preventDefault();
            const fileInput = document.getElementById('file-input');
            if (fileInput) {
              try { fileInput.click(); } catch (e) { if (showUploadModalButton) showUploadModalButton.click(); }
            } else if (showUploadModalButton) {
              showUploadModalButton.click();
            }
          });

          // Export: download current preview table as CSV
          const btnExport = document.getElementById('btn-export');
          if (btnExport) btnExport.addEventListener('click', (ev) => {
            ev.preventDefault();
            try {
              const tbl = document.getElementById('side-excel-preview-table');
              if (!tbl) { alert('No preview table available'); return; }
              const rows = Array.from(tbl.querySelectorAll('tr'));
              const csv = rows.map(r => Array.from(r.querySelectorAll('th,td')).map(cell => '"' + String(cell.textContent || '').replace(/"/g, '""') + '"').join(',')).join('\n');
              const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
              const url = URL.createObjectURL(blob);
              const a = document.createElement('a');
              a.href = url;
              a.download = (window.activeVendorStateName ? window.activeVendorStateName + '_preview.csv' : 'preview.csv');
              document.body.appendChild(a);
              a.click();
              a.remove();
              URL.revokeObjectURL(url);
            } catch (e) {
              console.warn('Export failed', e);
              alert('Export failed: ' + (e && e.message));
            }
          });

          // Save: collect mapping inputs and store in localStorage (includes assigned rule info)
          const btnSave = document.getElementById('btn-save');
          if (btnSave) btnSave.addEventListener('click', (ev) => {
            ev.preventDefault();
            try {
              const inputs = Array.from(document.querySelectorAll('#side-excel-preview-columns-list input'));
              const map = inputs.map(inp => {
                let names = null;
                let patterns = null;
                if (inp.dataset.assignedRuleNames) {
                  try { names = JSON.parse(inp.dataset.assignedRuleNames); } catch (e) { names = null; }
                }
                if (inp.dataset.assignedRulePatterns) {
                  try { patterns = JSON.parse(inp.dataset.assignedRulePatterns); } catch (e) { patterns = null; }
                }
                return {
                  index: inp.dataset.colIndex,
                  value: inp.value,
                  assignedRuleNames: names,
                  assignedRulePatterns: patterns
                };
              });
              const key = 'column_mapping_' + (window.activeVendorStateName || 'unknown');
              localStorage.setItem(key, JSON.stringify(map));
              alert('Mapping saved locally.');
            } catch (e) { console.warn('Save mapping failed', e); alert('Save failed'); }
          });

          // Upload Dataset: open upload modal (pre-fill state)
          const btnUpload = document.getElementById('btn-upload-dataset');
          if (btnUpload) btnUpload.addEventListener('click', (ev) => {
            ev.preventDefault();
            try {
              if (uploadModal) uploadModal.dataset.forState = window.activeVendorStateName || '';
              if (showUploadModalButton) showUploadModalButton.click(); else if (uploadModal) uploadModal.classList.remove('hidden');
            } catch (e) { console.warn('Open upload modal failed', e); }
          });
        } catch (e) { console.warn('Wiring toolbar failed', e); }
      } catch (e) {
        console.warn('[renderSelectedStateHeader] failed to create side preview container', e);
      }
    } else {
      // ensure it is positioned after header for predictable layout
      try {
        if (header && createdSidePreview && header.parentNode && createdSidePreview.parentNode === header.parentNode) {
          const after = header.nextElementSibling;
          if (after !== createdSidePreview) header.parentNode.insertBefore(createdSidePreview, header.nextSibling);
        }
      } catch (e) { /* ignore */ }
    }

    // Wire up upload button inside header
    const uploadBtn = document.getElementById('state-upload-button');
    if (uploadBtn) {
      // remove existing listeners to avoid duplicates
      uploadBtn.replaceWith(uploadBtn.cloneNode(true));
      const newUploadBtn = document.getElementById('state-upload-button');
      if (newUploadBtn) {
        newUploadBtn.addEventListener('click', (ev) => {
          ev.preventDefault();
          if (uploadModal) uploadModal.dataset.forState = upper;
          if (showUploadModalButton) {
            showUploadModalButton.click();
          } else if (uploadModal) {
            uploadModal.classList.remove('hidden');
          }
        });
      }
    }
  }

  // expose renderer to other parts of file
  window._renderSelectedStateHeader = renderSelectedStateHeader;

  // --- Vendor list click handler: select a state and show header ---
  vendorListEl.addEventListener('click', (e) => {
    const link = e.target.closest('.nav-link');
    if (!link) return;
    e.preventDefault();

    // get state name from dataset (preferred) or text
    const stateName = (link.dataset.stateName || link.textContent || '').trim();
    if (!stateName) return;

    // If stateName is already the 2-letter initials, use it as-is
    const initialsMatch = stateName.match(/^[A-Za-z]{2}$/);
    const initials = initialsMatch ? initialsMatch[0].toUpperCase() : stateName.slice(0, 2).toUpperCase();

    // set global trackers so other code can access display name / id
    window.activeVendorStateName = initials;
    if (link.dataset.vendorId) {
      window.activeVendorId = link.dataset.vendorId;
      activeVendorId = link.dataset.vendorId;
    } else {
      // fallback synthetic id
      window.activeVendorId = `state-${initials}`;
      activeVendorId = window.activeVendorId;
    }

    // ensure selected visual state in sidebar
    document.querySelectorAll('#vendor-list .nav-link').forEach(nav => nav.classList.remove('bg-slate-700', 'text-white'));
    link.classList.add('bg-slate-700', 'text-white');

    // render header and hide welcome
    renderSelectedStateHeader(initials);

    // trigger existing app behavior for selecting vendor
    if (typeof renderCombinedRules === 'function') renderCombinedRules();
    if (typeof showAccountManagementView === 'function') showAccountManagementView(window.activeVendorId);
  });

  // --- Optional: When switching back to the "Summary" page, restore welcome view ---
  if (sidebarNav) {
    sidebarNav.addEventListener('click', (e) => {
      const link = e.target.closest('.nav-link');
      if (!link) return;
      const pageId = link.dataset.page;
      if (pageId === 'uploader-page') {
        // ensure header is visible if a state was previously selected
        const header = document.getElementById('selected-state-header');
        if (!header && window.activeVendorStateName) {
          renderSelectedStateHeader(window.activeVendorStateName);
        }
      } else if (pageId === 'summary-page') {
        // restore the welcome message when leaving configurator
        if (welcomeView) welcomeView.classList.remove('hidden');
        const header = document.getElementById('selected-state-header');
        if (header) header.remove();
        // Clear any selected vendor highlight and reset active globals so summary shows no selection
        try {
          document.querySelectorAll('#vendor-list .nav-link').forEach(nav => nav.classList.remove('bg-slate-700', 'text-white'));
          window.activeVendorId = null;
          window.activeVendorStateName = '';
        } catch (e) { /* ignore */ }
      }
    });
  }
})();

/* -------------------------
   Upload Modal — Robust Browse + Drag-Drop + Upload Simulation
   Replace your existing wireUploadModal() IIFE with this block.
------------------------- */
(function wireUploadModal() {
  const uploadModal = document.getElementById('upload-modal');
  const dropZone = document.getElementById('drop-zone');
  const fileInput = document.getElementById('file-input');
  const uploadButton = document.getElementById('upload-button');
  const fileNameEl = document.getElementById('file-name');
  const fileSizeEl = document.getElementById('file-size');
  const removeFileBtn = document.getElementById('remove-file-btn');
  const progressBar = document.getElementById('progress-bar');
  const progressText = document.getElementById('progress-text');
  const uploadProgressSection = document.getElementById('upload-progress-section');
  const initialStatePanel = document.getElementById('initial-state');
  const closeUploadModalBtn = document.getElementById('close-upload-modal-button');
  const showUploadModalButton = document.getElementById('show-upload-modal-button');
  const browseBtn = document.getElementById('browse-file-btn');

  let selectedFile = null;
  let _fileInputOriginallyHidden = false;

  // === Modal controls ===
  function openUploadModal(forState) {
    if (!uploadModal) return;
    if (forState) uploadModal.dataset.forState = forState;
    uploadModal.classList.remove('hidden');
    clearSelectedFile();
    console.log('Upload modal opened for state:', forState);

    // Ensure the selected state in the sidebar stays highlighted while user is in configurator/upload flow
    try {
      const stateToken = (forState || window.activeVendorStateName || '').toString().trim().toUpperCase();
      if (stateToken) {
        // update global
        window.activeVendorStateName = stateToken;
        // remove highlight from other entries
        const sidebar = document.getElementById('vendor-list');
        if (sidebar) {
          sidebar.querySelectorAll('.nav-link').forEach(nav => nav.classList.remove('bg-slate-700', 'text-white'));
          // try matching by multiple attributes (data-state-name or data-vendor-id)
          let match = sidebar.querySelector(`a[data-state-name="${stateToken}"]`) || sidebar.querySelector(`a[data-vendor-id="state-${stateToken}"]`) || sidebar.querySelector(`a[data-vendor-id="${stateToken}"]`);
          if (match) {
              try {
                match.classList.add('bg-slate-700', 'text-white');
                // ensure global vendor id is set so other UI code knows the active vendor
                window.activeVendorId = match.dataset.vendorId || match.getAttribute('data-vendor-id') || `state-${stateToken}`;
                // scroll into view in case the sidebar is long
                try { match.scrollIntoView({ block: 'center', behavior: 'auto' }); } catch (e) {}
              } catch (e) { console.warn('Failed to set sidebar highlight (openUploadModal)', e); }
            }
        }

        // render the selected-state header if helper exists (keeps UI consistent)
        if (typeof window._renderSelectedStateHeader === 'function') {
          try { window._renderSelectedStateHeader(stateToken); } catch (e) { /* ignore */ }
        }
        // hide welcome view if present
        const welcomeView = document.getElementById('welcome-view');
        if (welcomeView) welcomeView.classList.add('hidden');
      }
    } catch (e) { console.warn('Failed to highlight selected state on openUploadModal', e); }
  }

  function closeUploadModal() {
    if (!uploadModal) return;
    uploadModal.classList.add('hidden');
    delete uploadModal.dataset.forState;
    clearSelectedFile();
    if (progressBar) progressBar.style.width = '0%';
    if (progressText) progressText.textContent = '';
  }

  // === Helpers ===
  function bytesToHuman(bytes) {
    if (bytes === 0) return '0 B';
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 2)} ${units[i]}`;
  }

  function showSelectedFile(file) {
    selectedFile = file;
    if (fileNameEl) fileNameEl.textContent = file.name;
    if (fileSizeEl) fileSizeEl.textContent = bytesToHuman(file.size);
    if (uploadProgressSection) uploadProgressSection.classList.remove('hidden');
    if (initialStatePanel) initialStatePanel.classList.add('hidden');
    if (uploadButton) uploadButton.disabled = false;
    console.log('Selected file:', file.name);
  }

  function clearSelectedFile() {
    selectedFile = null;
    if (fileNameEl) fileNameEl.textContent = '';
    if (fileSizeEl) fileSizeEl.textContent = '';
    if (fileInput) {
      // restore original hidden state if we changed it
      if (_fileInputOriginallyHidden) {
        fileInput.classList.add('hidden');
        fileInput.style.position = '';
        fileInput.style.left = '';
        fileInput.style.top = '';
        fileInput.style.width = '';
        fileInput.style.height = '';
        fileInput.style.opacity = '';
        _fileInputOriginallyHidden = false;
      }
      try { fileInput.value = ''; } catch (e) { /* ignore */ }
    }
    if (uploadButton) uploadButton.disabled = true;
    if (uploadProgressSection) uploadProgressSection.classList.add('hidden');
    if (initialStatePanel) initialStatePanel.classList.remove('hidden');
  }

  // === Make file input reliably clickable even if hidden by Tailwind's `hidden` ===
  function ensureFileInputClickableTemporarily() {
    if (!fileInput) return false;
    // If the element currently has the `hidden` class (display:none), we temporarily make it off-screen visible
    if (fileInput.classList.contains('hidden') || getComputedStyle(fileInput).display === 'none') {
      _fileInputOriginallyHidden = true;
      fileInput.classList.remove('hidden');
      // keep it off-screen and non-interfering with layout
      fileInput.style.position = 'absolute';
      fileInput.style.left = '-9999px';
      fileInput.style.top = '0';
      fileInput.style.width = '1px';
      fileInput.style.height = '1px';
      fileInput.style.opacity = '0';
      // keep pointer-events enabled so click() works
    }
    return true;
  }

  // === Browse button click — robust fix ===
  if (browseBtn && fileInput) {
    const openPicker = (e) => {
      if (e) { e.preventDefault(); e.stopPropagation(); }
      console.log('Browse button clicked → attempting to open file input');
      if (!ensureFileInputClickableTemporarily()) {
        alert('File input is not available. Open console for details.');
        return;
      }
      try {
        // This is a user gesture; should open the native picker
        fileInput.click();
      } catch (err) {
        console.error('Failed to trigger file input click from Browse button:', err);
        alert('Unable to open file picker. Please check your browser settings or use the upload button.');
      }
    };
    browseBtn.addEventListener('click', openPicker);
    // mobile touch fallback
    browseBtn.addEventListener('touchstart', openPicker);
  }

  // === Click anywhere in drop zone (except the Browse button) ===
  if (dropZone && fileInput) {
    dropZone.addEventListener('click', (e) => {
      // Prevent triggering when Browse button clicked
      if (e.target && e.target.id === 'browse-file-btn') {
        return;
      }
      e.preventDefault();
      console.log('Drop zone clicked → opening file picker');
      if (!ensureFileInputClickableTemporarily()) {
        alert('File input is not available. Open console for details.');
        return;
      }
      try {
        fileInput.click();
      } catch (err) {
        console.error('Failed to open file picker from dropZone:', err);
        alert('Unable to open file picker. Please try the "Browse files" button.');
      }
    });
  }

  // === Drag & drop support ===
  if (dropZone) {
    dropZone.addEventListener('dragover', (e) => {
      e.preventDefault();
      dropZone.classList.add('drag-over');
    });
    dropZone.addEventListener('dragleave', (e) => {
      e.preventDefault();
      dropZone.classList.remove('drag-over');
    });
    dropZone.addEventListener('drop', (e) => {
      e.preventDefault();
      dropZone.classList.remove('drag-over');
      const file = e.dataTransfer.files && e.dataTransfer.files[0];
      if (file) showSelectedFile(file);
    });
  }

  // === File input change ===
  if (fileInput) {
    fileInput.addEventListener('change', (e) => {
      const file = e.target.files && e.target.files[0];
      if (!file) {
        // user cancelled the picker — restore hidden state if we manipulated it
        if (_fileInputOriginallyHidden) {
          fileInput.classList.add('hidden');
          fileInput.style.position = '';
          fileInput.style.left = '';
          fileInput.style.top = '';
          fileInput.style.width = '';
          fileInput.style.height = '';
          fileInput.style.opacity = '';
          _fileInputOriginallyHidden = false;
        }
        return;
      }
      showSelectedFile(file);
      // restore hidden state after successful selection to keep UI consistent
      if (_fileInputOriginallyHidden) {
        setTimeout(() => {
          try {
            fileInput.classList.add('hidden');
            fileInput.style.position = '';
            fileInput.style.left = '';
            fileInput.style.top = '';
            fileInput.style.width = '';
            fileInput.style.height = '';
            fileInput.style.opacity = '';
          } catch (e) { /* ignore */ }
          _fileInputOriginallyHidden = false;
        }, 200);
      }
    });
  }

  // === Remove file ===
  if (removeFileBtn) {
    removeFileBtn.addEventListener('click', (e) => {
      e.preventDefault();
      clearSelectedFile();
    });
  }

  // === Close modal ===
  if (closeUploadModalBtn) {
    closeUploadModalBtn.addEventListener('click', (e) => {
      e.preventDefault();
      closeUploadModal();
    });
  }

  // === Open modal (from Rule Configurator) ===
  if (showUploadModalButton) {
    showUploadModalButton.addEventListener('click', (e) => {
      e.preventDefault();
      const forState = uploadModal.dataset.forState || window.activeVendorStateName || '';
      openUploadModal(forState);
    });
  }

  // === Upload simulation / Upload button now opens picker when no file selected ===
  if (uploadButton) {
    uploadButton.disabled = true;
    uploadButton.addEventListener('click', async (e) => {
      e.preventDefault();

      // If no file is selected, treat Upload as "open file picker"
      if (!selectedFile) {
        if (!fileInput) {
          alert('File input not found in DOM.');
          return;
        }
        if (!ensureFileInputClickableTemporarily()) {
          alert('Unable to open file picker. See console for details.');
          return;
        }
        try {
          fileInput.click();
          return;
        } catch (err) {
          console.error('Failed to open file picker from Upload button:', err);
          alert('Unable to open file picker. Please use the "Browse files" link.');
          return;
        }
      }

      // proceed with simulated upload (keeps previous behavior)
      const stateFor = uploadModal.dataset.forState || window.activeVendorStateName || '';
      console.log(`Uploading file for state: ${stateFor}`, selectedFile);

      const fd = new FormData();
      fd.append('file', selectedFile);
      if (stateFor) fd.append('state', stateFor);

      try {
        uploadButton.disabled = true;
        if (progressText) progressText.textContent = 'Uploading...';
        if (progressBar) progressBar.style.width = '0%';

        let percent = 0;
        const interval = setInterval(() => {
          percent += 10;
          if (percent > 100) percent = 100;
          if (progressBar) progressBar.style.width = `${percent}%`;
          if (progressText) progressText.textContent = `${percent}%`;
          if (percent === 100) {
            clearInterval(interval);
            if (progressText) progressText.textContent = 'Upload complete!';
            setTimeout(() => {
              alert('File uploaded successfully!');
              closeUploadModal();
              // After upload completes, ensure the uploaded-for state remains highlighted in sidebar
              try {
                const sf = (stateFor || window.activeVendorStateName || '').toString().trim().toUpperCase();
                if (sf) {
                  window.activeVendorStateName = sf;
                  const sidebar = document.getElementById('vendor-list');
                  if (sidebar) {
                    sidebar.querySelectorAll('.nav-link').forEach(nav => nav.classList.remove('bg-slate-700', 'text-white'));
                    const match = sidebar.querySelector(`a[data-state-name="${sf}"]`) || sidebar.querySelector(`a[data-vendor-id="state-${sf}"]`) || sidebar.querySelector(`a[data-vendor-id="${sf}"]`);
                    if (match) {
                      try {
                        match.classList.add('bg-slate-700', 'text-white');
                        window.activeVendorId = match.dataset.vendorId || match.getAttribute('data-vendor-id') || `state-${sf}`;
                        try { match.scrollIntoView({ block: 'center', behavior: 'auto' }); } catch (e) {}
                      } catch (e) { console.warn('Failed to set sidebar highlight (after upload)', e); }
                    }
                  }
                  if (typeof window._renderSelectedStateHeader === 'function') {
                    try { window._renderSelectedStateHeader(sf); } catch (e) {}
                  }
                }
              } catch (e) { console.warn('Failed to highlight state after upload', e); }
            }, 700);
          }
        }, 150);
      } catch (err) {
        console.error('Upload failed', err);
        alert('Upload failed: ' + (err.message || err));
      }
    });
  }

  // expose modal opener globally
  window.openUploadModal = openUploadModal;
})();

/* ===========================
   Robust overlay file-input (highest success rate)
   - Paste at end of js/rule_configurator.js (after other upload wiring)
   =========================== */
(function ensureBrowseOpensViaOverlay() {
  const browseBtn = document.getElementById('browse-file-btn');
  const originalFileInput = document.getElementById('file-input');
  if (!browseBtn) { console.warn('[overlay-fix] #browse-file-btn not found'); return; }
  if (!originalFileInput) { console.warn('[overlay-fix] #file-input not found'); return; }

  // If overlay already exists, do nothing (idempotent)
  if (document.getElementById('_rc_file_overlay')) {
    console.log('[overlay-fix] overlay already installed');
    return;
  }

  // Create an overlay input (a new input element). We do not modify the original element's "hidden" class.
  const overlay = document.createElement('input');
  overlay.type = 'file';
  overlay.id = '_rc_file_overlay';
  // copy accept attribute if present (so same file types allowed)
  if (originalFileInput.getAttribute('accept')) overlay.setAttribute('accept', originalFileInput.getAttribute('accept'));
  overlay.style.position = 'absolute';
  overlay.style.opacity = '0';
  overlay.style.cursor = 'pointer';
  overlay.style.zIndex = '9999999';
  overlay.style.width = '1px'; // will be resized/positioned immediately
  overlay.style.height = '1px';
  overlay.style.pointerEvents = 'auto';
  // keep overlay out of tab order
  overlay.tabIndex = -1;

  // Append overlay to body (will be positioned over button)
  document.body.appendChild(overlay);

  // Position overlay directly on top of the browse button when mouse moves over it or on window resize/scroll
  function positionOverlay() {
    try {
      const rect = browseBtn.getBoundingClientRect();
      // place overlay exactly over the button (small padding to be safe)
      overlay.style.left = `${Math.max(0, window.scrollX + rect.left - 2)}px`;
      overlay.style.top = `${Math.max(0, window.scrollY + rect.top - 2)}px`;
      overlay.style.width = `${Math.max(8, rect.width + 4)}px`;
      overlay.style.height = `${Math.max(8, rect.height + 4)}px`;
      overlay.style.display = 'block';
      // ensure it's on top
      overlay.style.zIndex = '9999999';
    } catch (err) {
      console.warn('[overlay-fix] positionOverlay failed', err);
    }
  }

  // Keep overlay positioned while user may click — update on scroll/resize and when pointer moves near button.
  window.addEventListener('resize', positionOverlay, { passive: true });
  window.addEventListener('scroll', positionOverlay, { passive: true });
  // Update right away and also on mouseenter to handle dynamic layout
  browseBtn.addEventListener('mouseenter', positionOverlay, { passive: true });
  browseBtn.addEventListener('mousemove', positionOverlay, { passive: true });

  // If the modal opens/closes, ensure overlay is shown/hidden accordingly
  const uploadModal = document.getElementById('upload-modal');
  const observer = new MutationObserver(() => {
    if (!uploadModal) return;
    const isHidden = uploadModal.classList.contains('hidden');
    overlay.style.display = isHidden ? 'none' : 'block';
    // reposition when modal becomes visible
    if (!isHidden) setTimeout(positionOverlay, 40);
  });
  if (uploadModal) observer.observe(uploadModal, { attributes: true, attributeFilter: ['class'] });

  // Forward change events from overlay to existing handlers by reusing original input's change logic:
  // - If your code listens on the original file-input element (id='file-input'), we trigger a synthetic change event on it.
  overlay.addEventListener('change', (ev) => {
    try {
      // If overlay has files, copy them to original input via DataTransfer when possible (best-effort)
      const file = overlay.files && overlay.files[0];
      console.log('[overlay-fix] overlay selected file:', file && file.name);
      // Call the existing showSelectedFile handler if present (wireUploadModal sets showSelectedFile in same file)
      // We attempt to call a global function showSelectedFile(file) if it exists.
      if (typeof showSelectedFile === 'function') {
        showSelectedFile(file);
      } else {
        // fallback: try to call original input's change handler by creating a DataTransfer and assigning files (not always possible)
        try {
          // Many browsers do not allow programmatic assignment to input.files. So we simply dispatch an event with reference to file in window
          window.__rc_last_file_selected = file;
          const evt = new Event('change', { bubbles: true, cancelable: true });
          originalFileInput.dispatchEvent(evt);
        } catch (e) {
          console.warn('[overlay-fix] fallback dispatch failed', e);
        }
      }
    } catch (err) {
      console.error('[overlay-fix] overlay change handler failed', err);
    } finally {
      // Reset overlay input so selecting same file again will still fire change
      try { overlay.value = ''; } catch (e) { /* ignore */ }
    }
  });

  // Clicking the overlay is the same as clicking the browse button - but keep the native button's click behavior too
  browseBtn.addEventListener('click', () => {
    // ensure overlay is positioned before user click
    positionOverlay();
  }, { passive: true });

  // Ensure overlay visible only when uploader modal is open (if you use upload modal)
  if (uploadModal) {
    const isOpen = !uploadModal.classList.contains('hidden');
    overlay.style.display = isOpen ? 'block' : 'none';
  } else {
    // if modal absent, always show overlay (so browse works)
    overlay.style.display = 'block';
  }

  // initial position
  setTimeout(positionOverlay, 30);

  // Provide helper to remove overlay if you want to revert
  window._rc_remove_file_overlay = function() {
    try {
      observer.disconnect();
      window.removeEventListener('resize', positionOverlay);
      window.removeEventListener('scroll', positionOverlay);
      browseBtn.removeEventListener('mouseenter', positionOverlay);
      browseBtn.removeEventListener('mousemove', positionOverlay);
      overlay.remove();
      delete window._rc_remove_file_overlay;
      console.log('[overlay-fix] overlay removed');
    } catch (e) {
      console.warn('[overlay-fix] remove overlay failed', e);
    }
  };

  console.log('[overlay-fix] initialized — overlay input positioned over Browse button');
})();

/* ===========================
   Auto-upload on file selection + progress + in-modal success
   Paste at the end of js/rule_configurator.js
   =========================== */
(function enableAutoUploadOnSelect() {
  const fileInput = document.getElementById('file-input');
  const uploadModal = document.getElementById('upload-modal');
  const uploadButton = document.getElementById('upload-button');
  const progressBar = document.getElementById('progress-bar');
  const progressText = document.getElementById('progress-text');
  const uploadProgressSection = document.getElementById('upload-progress-section');
  const initialStatePanel = document.getElementById('initial-state');
  const fileNameEl = document.getElementById('file-name');
  const fileSizeEl = document.getElementById('file-size');
  const statusMessageEl = document.getElementById('status-message');
  const closeUploadModalBtn = document.getElementById('close-upload-modal-button');

  if (!fileInput) {
    console.warn('[auto-upload] file input not found; skipping auto-upload wiring.');
    return;
  }

  // Utility: human-readable size (copied from earlier helpers)
  function bytesToHuman(bytes) {
    if (!bytes && bytes !== 0) return '';
    if (bytes === 0) return '0 B';
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 2)} ${units[i]}`;
  }

  // UI helper to show selected file (safe if your wireUploadModal already defines one)
  function showFileInModal(file) {
    if (!file) return;
    try {
      if (fileNameEl) fileNameEl.textContent = file.name;
      if (fileSizeEl) fileSizeEl.textContent = bytesToHuman(file.size);
      if (uploadProgressSection) uploadProgressSection.classList.remove('hidden');
      if (initialStatePanel) initialStatePanel.classList.add('hidden');
      if (uploadButton) uploadButton.disabled = false;
      if (statusMessageEl) statusMessageEl.textContent = '';
    } catch (e) {
      console.warn('[auto-upload] showFileInModal error', e);
    }
  }

  // Show success info inside modal
  function showSuccessInModal(message) {
    if (progressText) progressText.textContent = '100%';
    if (progressBar) progressBar.style.width = '100%';
    if (statusMessageEl) {
      statusMessageEl.classList.remove('text-red-600');
      statusMessageEl.classList.add('text-green-600');
      statusMessageEl.textContent = message || 'File uploaded successfully!';
    }
    // quick visual confirmation. If two-step upload flow is NOT active, close the modal.
    // When two-step flow is active we keep the modal open so user can Confirm Upload.
    if (!window._rc_two_step_flow) {
      setTimeout(() => {
        try {
          if (uploadModal) uploadModal.classList.add('hidden');
        } catch (e) { /* ignore */ }
      }, 900);
    }
  }

  // Show error inside modal (keeps modal open so user can retry)
  function showErrorInModal(message) {
    if (statusMessageEl) {
      statusMessageEl.classList.remove('text-green-600');
      statusMessageEl.classList.add('text-red-600');
      statusMessageEl.textContent = message || 'Upload failed';
    }
    if (uploadButton) uploadButton.disabled = false;
    if (progressText) progressText.textContent = '';
  }

  // Perform real upload with XHR to show progress. Returns a Promise.
  // Tries API_BASE_URL + '/api/upload' if API_BASE_URL exists, otherwise '/api/upload'.
  function performXHRUpload(file) {
    return new Promise((resolve, reject) => {
      try {
        const base = (typeof API_BASE_URL !== 'undefined' && API_BASE_URL) ? API_BASE_URL : '';
        const url = `${base}/api/upload`;

        const fd = new FormData();
        fd.append('file', file);
        // include state context if set on modal (so server knows which state)
        const forState = uploadModal && uploadModal.dataset && uploadModal.dataset.forState ? uploadModal.dataset.forState : (window.activeVendorStateName || '');
        if (forState) fd.append('state', forState);

        const xhr = new XMLHttpRequest();
        xhr.open('POST', url, true);
        // include cookies for auth if your backend uses them
        xhr.withCredentials = true;

        xhr.upload.onprogress = function (evt) {
          if (!evt.lengthComputable) return;
          const pct = Math.round((evt.loaded / evt.total) * 100);
          if (progressBar) progressBar.style.width = `${pct}%`;
          if (progressText) progressText.textContent = `${pct}%`;
        };

        xhr.onload = function () {
          if (xhr.status >= 200 && xhr.status < 300) {
            let json = null;
            try { json = JSON.parse(xhr.responseText); } catch (e) { /* not JSON */ }
            resolve(json || { ok: true, status: xhr.status });
          } else {
            reject(new Error(`Upload failed (status ${xhr.status})`));
          }
        };

        xhr.onerror = function () {
          reject(new Error('Network error during upload'));
        };

        xhr.send(fd);
      } catch (err) {
        reject(err);
      }
    });
  }

  // Simulated upload (fallback) — visually identical to earlier simulation but returns a Promise.
  function simulatedUpload(file) {
    return new Promise((resolve) => {
      let pct = 0;
      if (progressBar) progressBar.style.width = '0%';
      if (progressText) progressText.textContent = '0%';
      const t = setInterval(() => {
        pct += Math.floor(Math.random() * 12) + 8; // variable increments
        if (pct > 100) pct = 100;
        if (progressBar) progressBar.style.width = `${pct}%`;
        if (progressText) progressText.textContent = `${pct}%`;
        if (pct >= 100) {
          clearInterval(t);
          setTimeout(() => resolve({ ok: true, simulated: true }), 350);
        }
      }, 140);
    });
  }

  // Main trigger used after file selection
  async function handleFileSelected(file) {
    if (!file) return;
    // show in modal UI
    showFileInModal(file);

    // disable upload button to avoid double-submits
    if (uploadButton) uploadButton.disabled = true;

    // clear status area
    if (statusMessageEl) {
      statusMessageEl.textContent = '';
      statusMessageEl.classList.remove('text-red-600', 'text-green-600');
    }

    // If your app already stores a global `selectedFile`, update it for compatibility
    try { window.selectedFile = file; } catch (e) { /* ignore */ }

    // Try real upload first; if it errors, fallback to simulated upload
    try {
      const result = await performXHRUpload(file);
      // success
      showSuccessInModal('File uploaded successfully!');
      console.log('[auto-upload] upload result', result);
    } catch (err) {
      console.warn('[auto-upload] real upload failed, falling back to simulated upload:', err);
      // fall back to simulated progress + success
      try {
        await simulatedUpload(file);
        showSuccessInModal('File uploaded (simulated). Server not reachable.');
      } catch (e) {
        console.error('[auto-upload] simulated upload failed unexpectedly', e);
        showErrorInModal('Unexpected upload error. See console.');
      }
    } finally {
      // Reset input so selecting the same file again triggers change
      try { fileInput.value = ''; } catch (e) { /* ignore */ }
    }
  }

  // Wire: when user picks via the actual file input (overlay or regular), auto-start upload
  fileInput.addEventListener('change', (ev) => {
    const f = ev.target.files && ev.target.files[0];
    if (!f) {
      // maybe user canceled — do nothing
      return;
    }
    // Immediately handle file selection (no further user action required)
    handleFileSelected(f);
  });

  // Also support the overlay element if created earlier (_rc_file_overlay)
  const overlay = document.getElementById('_rc_file_overlay');
  if (overlay) {
    overlay.addEventListener('change', (ev) => {
      const f = ev.target.files && ev.target.files[0] || window.__rc_last_file_selected;
      if (f) handleFileSelected(f);
    });
  }

  // If modal is reopened, clear old UI
  if (uploadModal) {
    // observe class changes to reinitialize UI when modal opens
    const mo = new MutationObserver(() => {
      if (!uploadModal) return;
      if (!uploadModal.classList.contains('hidden')) {
        // reset progress and messages when opening
        try {
          if (progressBar) progressBar.style.width = '0%';
          if (progressText) progressText.textContent = '';
          if (statusMessageEl) statusMessageEl.textContent = '';
          if (initialStatePanel) initialStatePanel.classList.remove('hidden');
          if (uploadProgressSection) uploadProgressSection.classList.add('hidden');
        } catch (e) { /* ignore */ }
      }
    });
    mo.observe(uploadModal, { attributes: true, attributeFilter: ['class'] });
  }

  // optional: clicking built-in Upload button should use the existing upload flow (if user prefers)
  if (uploadButton) {
    uploadButton.addEventListener('click', (ev) => {
      // If a file is already selected in input.files, handle it; otherwise open picker (existing logic may handle)
      const f = (fileInput && fileInput.files && fileInput.files[0]) || window.selectedFile || null;
      if (f) {
        // prevent double-binding with the current handler
        ev.preventDefault();
        handleFileSelected(f);
      } // else let other logic open picker
    });
  }

  console.log('[auto-upload] automatic upload on file selection enabled.');
})();

/* ===========================
   Two-step Upload Flow:
   1) Select file -> automatic transfer to /api/upload (progress bar)
   2) After transfer completes, keep modal open, enable Upload (Confirm) button (blue)
   3) User clicks Confirm -> final local confirmation & optional clean-up
   Paste at end of js/rule_configurator.js
   =========================== */
(function twoStepUploadFlow() {
  const fileInput = document.getElementById('file-input');
  const overlay = document.getElementById('_rc_file_overlay'); // optional overlay
  const uploadModal = document.getElementById('upload-modal');
  const uploadButton = document.getElementById('upload-button');
  const progressBar = document.getElementById('progress-bar');
  const progressText = document.getElementById('progress-text');
  const uploadProgressSection = document.getElementById('upload-progress-section');
  const initialStatePanel = document.getElementById('initial-state');
  const fileNameEl = document.getElementById('file-name');
  const fileSizeEl = document.getElementById('file-size');
  const statusMessageEl = document.getElementById('status-message');

  if (!fileInput || !uploadButton) {
    console.warn('[2step-upload] file input or upload button not found — skipping 2-step wiring.');
    return;
  }

  // mark two-step flow active so other upload handlers (auto-upload) don't auto-close modal
  window._rc_two_step_flow = true;
  if (uploadModal && uploadModal.dataset) uploadModal.dataset.twoStep = '1';

  // local state
  let stagedFile = null;         // File object user selected
  let stagedUploadResult = null; // server response after initial transfer
  let isTransferComplete = false;
  let isFinalized = false;

  // helper for human-readable file size
  function bytesToHuman(bytes) {
    if (!bytes && bytes !== 0) return '';
    if (bytes === 0) return '0 B';
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 2)} ${units[i]}`;
  }

  function resetModalUIOnOpen() {
    stagedFile = null;
    stagedUploadResult = null;
    isTransferComplete = false;
    isFinalized = false;
    if (fileNameEl) fileNameEl.textContent = '';
    if (fileSizeEl) fileSizeEl.textContent = '';
    if (progressBar) progressBar.style.width = '0%';
    if (progressText) progressText.textContent = '';
    if (statusMessageEl) {
      statusMessageEl.textContent = '';
      statusMessageEl.classList.remove('text-green-600', 'text-red-600');
    }
    // Disable Upload (grey) until the transfer completes
    uploadButton.disabled = true;
    uploadButton.classList.remove('bg-blue-600', 'hover:bg-blue-700', 'cursor-pointer');
    uploadButton.classList.add('bg-gray-400', 'cursor-not-allowed');
    // label to "Upload" (default)
    uploadButton.textContent = uploadButton.getAttribute('data-label') || uploadButton.textContent || 'Upload';
    if (initialStatePanel) initialStatePanel.classList.remove('hidden');
    if (uploadProgressSection) uploadProgressSection.classList.add('hidden');
  }

  // Initialize when modal opens (if you use upload modal, observe it)
  if (uploadModal) {
    const mo = new MutationObserver(() => {
      if (!uploadModal) return;
      if (!uploadModal.classList.contains('hidden')) {
        // modal opened
        setTimeout(resetModalUIOnOpen, 20);
      }
    });
    mo.observe(uploadModal, { attributes: true, attributeFilter: ['class'] });
  } else {
    // if no modal object, just reset UI once now
    resetModalUIOnOpen();
  }

  // show selected file metadata in modal and prepare for transfer
  function showSelectedFile(file) {
    stagedFile = file;
    isTransferComplete = false;
    stagedUploadResult = null;
    if (fileNameEl) fileNameEl.textContent = file.name;
    if (fileSizeEl) fileSizeEl.textContent = bytesToHuman(file.size);
    if (uploadProgressSection) uploadProgressSection.classList.remove('hidden');
    if (initialStatePanel) initialStatePanel.classList.add('hidden');
    if (progressBar) progressBar.style.width = '0%';
    if (progressText) progressText.textContent = '';
    if (statusMessageEl) {
      statusMessageEl.textContent = '';
      statusMessageEl.classList.remove('text-green-600', 'text-red-600');
    }

    // Immediately begin transfer to server (so the progress bar shows real upload)
    // but keep Upload button disabled and grey until transfer finished
    uploadButton.disabled = true;
    uploadButton.classList.remove('bg-blue-600', 'hover:bg-blue-700', 'cursor-pointer');
    uploadButton.classList.add('bg-gray-400', 'cursor-not-allowed');

    // start transfer
    transferFileToServer(file).catch((err) => {
      console.error('[2step-upload] transferFileToServer error', err);
      if (statusMessageEl) {
        statusMessageEl.textContent = `Transfer failed: ${err.message || err}`;
        statusMessageEl.classList.add('text-red-600');
      }
      // keep Upload disabled so user can retry selecting a file
      isTransferComplete = false;
      stagedUploadResult = null;
      // leave progress bar as-is (partial)
    });
  }

  // low-level upload -> returns Promise that resolves with server response (or throws)
  function transferFileToServer(file) {
    return new Promise((resolve, reject) => {
      const base = (typeof API_BASE_URL !== 'undefined' && API_BASE_URL) ? API_BASE_URL : '';
      const url = `${base}/api/upload`; // change if your backend route differs

      const fd = new FormData();
      fd.append('file', file);
      // include state if modal has dataset or global active state
      const forState = (uploadModal && uploadModal.dataset && uploadModal.dataset.forState) || window.activeVendorStateName || '';
      if (forState) fd.append('state', forState);

      const xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.withCredentials = true;

      xhr.upload.onprogress = function (evt) {
        if (!evt.lengthComputable) return;
        const pct = Math.round((evt.loaded / evt.total) * 100);
        if (progressBar) progressBar.style.width = `${pct}%`;
        if (progressText) progressText.textContent = `${pct}%`;
      };

      xhr.onload = function () {
        if (xhr.status >= 200 && xhr.status < 300) {
          let json = null;
          try { json = JSON.parse(xhr.responseText); } catch (e) { /* non-json ok */ }
          // mark staged upload as complete, enable Upload button (blue) and change label
          isTransferComplete = true;
          stagedUploadResult = json || { ok: true, status: xhr.status };
          // set UI to 100% just in case
          if (progressBar) progressBar.style.width = '100%';
          if (progressText) progressText.textContent = '100%';
          if (statusMessageEl) {
            statusMessageEl.textContent = 'Transfer complete. Press Confirm Upload to finish.';
            statusMessageEl.classList.remove('text-red-600');
            statusMessageEl.classList.add('text-slate-600'); // neutral instruction color
          }
          // enable Upload button (blue) and change label to "Confirm Upload"
          uploadButton.disabled = false;
          uploadButton.classList.remove('bg-gray-400', 'cursor-not-allowed');
          uploadButton.classList.add('bg-blue-600', 'hover:bg-blue-700', 'cursor-pointer');
          // swap text while preserving original label in data-label
          if (!uploadButton.getAttribute('data-label')) {
            uploadButton.setAttribute('data-label', uploadButton.textContent || 'Upload');
          }
          uploadButton.textContent = 'Confirm Upload';
          resolve(stagedUploadResult);
        } else {
          reject(new Error(`Transfer failed (status ${xhr.status})`));
        }
      };

      xhr.onerror = function () {
        reject(new Error('Network error during transfer'));
      };

      xhr.send(fd);
    });
  }

  // === Excel parsing + preview rendering (client-side) ===
  function parseExcelFile(file) {
    return new Promise((resolve, reject) => {
      if (!file) return reject(new Error('No file provided'));
      // Ensure SheetJS is present
      const XLSXLib = (typeof XLSX !== 'undefined') ? XLSX : (window && window.XLSX ? window.XLSX : null);
      if (!XLSXLib) return reject(new Error('XLSX library not found (include xlsx.full.min.js)'));

      const reader = new FileReader();
      reader.onload = function (e) {
        try {
          const data = e.target.result; // ArrayBuffer
          const wb = XLSXLib.read(data, { type: 'array' });
          const firstSheetName = wb.SheetNames && wb.SheetNames[0];
          if (!firstSheetName) return resolve({ rows: [] });
          const ws = wb.Sheets[firstSheetName];
          // use header:1 so we get raw rows as arrays
          const rows = XLSXLib.utils.sheet_to_json(ws, { header: 1, defval: '' });
          resolve({ rows });
        } catch (err) {
          reject(err);
        }
      };
      reader.onerror = function (ev) {
        reject(new Error('Failed to read file'));
      };
      // read as array buffer for XLSX
      reader.readAsArrayBuffer(file);
    });
  }

  function renderExcelPreview(rows) {
    try {
      // Prefer rendering into the right-side header preview area if present
      const sideContainer = document.getElementById('side-excel-preview');
      const sideTable = document.getElementById('side-excel-preview-table');
      const sideColumnsList = document.getElementById('side-excel-preview-columns-list');
      const modalContainer = document.getElementById('excel-preview');
      const modalTable = document.getElementById('excel-preview-table');
      const modalColumnsList = document.getElementById('excel-preview-columns-list');

      // choose rendering target (side preferred)
      const targetContainer = sideContainer || modalContainer;
      const targetTable = sideTable || modalTable;
      const targetColumnsList = sideColumnsList || modalColumnsList;
      if (!targetContainer || !targetTable || !targetColumnsList) return;

  // clear existing
  targetTable.innerHTML = '';
  targetColumnsList.innerHTML = '';
  // ensure the columns-list stacks items vertically (one label+input per line)
  try {
    // prefer a column flex layout
    targetColumnsList.classList.remove('flex', 'flex-wrap');
    targetColumnsList.classList.add('flex', 'flex-col', 'gap-3', 'mt-2');
  } catch (e) { /* ignore if classList not present */ }

      // Show up to first 5 rows
      const maxRows = Math.min(5, (rows && rows.length) ? rows.length : 0);
      if (maxRows === 0) {
        previewTable.innerHTML = '<tr><td class="p-2">No rows available in the sheet.</td></tr>';
        previewContainer.classList.remove('hidden');
        return;
      }

      // Determine max column count across preview rows
      let maxCols = 0;
      for (let i = 0; i < maxRows; i++) {
        const r = rows[i] || [];
        if (r.length > maxCols) maxCols = r.length;
      }

      // Build table header using third-row column names when available
  const thead = document.createElement('thead');
      const headRow = document.createElement('tr');
      // Use the third row (index 2) as header names if present
      const headerNames = rows && rows.length > 2 ? rows[2] : [];
      for (let c = 0; c < maxCols; c++) {
        const th = document.createElement('th');
        th.className = 'border px-2 py-1 text-left bg-slate-100 text-xs';
        const name = headerNames && headerNames[c] !== undefined && headerNames[c] !== null ? String(headerNames[c]).trim() : '';
        th.textContent = name || '';
        headRow.appendChild(th);
      }
      thead.appendChild(headRow);
      targetTable.appendChild(thead);

      const tbody = document.createElement('tbody');
      for (let r = 0; r < maxRows; r++) {
        const tr = document.createElement('tr');
        const row = rows[r] || [];
        for (let c = 0; c < maxCols; c++) {
          const td = document.createElement('td');
          td.className = 'border px-2 py-1 text-xs';
          const v = (row[c] !== undefined && row[c] !== null) ? String(row[c]) : '';
          td.textContent = v;
          tr.appendChild(td);
        }
        tbody.appendChild(tr);
      }
      targetTable.appendChild(tbody);

      // Columns from 3rd row (index 2) ONLY. Render them as a vertical list of
      // "Label - [long input box]" items below the preview table. Only render
      // entries for non-empty column names (do NOT render numbered fallbacks).
      const thirdRow = (rows && rows.length > 2) ? rows[2] : [];
      if (thirdRow && thirdRow.length) {
        for (let i = 0; i < thirdRow.length; i++) {
          const rawName = thirdRow[i];
          if (rawName === undefined || rawName === null) continue;
          const colNameStr = String(rawName).trim();
          if (colNameStr === '') continue; // skip empty/blank column names

          const fieldWrap = document.createElement('div');
          // inline: label - input on a single line; ensure full width so items don't sit side-by-side
          fieldWrap.className = 'w-full mb-3 flex items-center gap-2';

          const lbl = document.createElement('label');
          lbl.className = 'text-sm font-medium text-slate-700';
          // give label a fixed-ish width so inputs align; fallback to CSS if Tailwind not present
          lbl.style.minWidth = '160px';
          lbl.style.flex = '0 0 160px';
          lbl.textContent = colNameStr;

          const dash = document.createElement('span');
          dash.className = 'text-sm text-slate-500';
          dash.textContent = ' - ';

          const inp = document.createElement('input');
          inp.type = 'text';
          inp.dataset.colIndex = i;
          inp.className = 'flex-1 border border-slate-200 rounded-md px-3 py-2 text-sm';
          inp.placeholder = '';

          // Make each input a drop target for rules
          inp.addEventListener('dragover', (ev) => { ev.preventDefault(); });
          inp.addEventListener('drop', (ev) => {
            ev.preventDefault();
            try {
              const payload = ev.dataTransfer && ev.dataTransfer.getData && ev.dataTransfer.getData('text/plain');
              if (!payload) return;
              const data = JSON.parse(payload);
              // retrieve existing arrays from dataset
              let names = [];
              let patterns = [];
              if (inp.dataset.assignedRuleNames) {
                try { names = JSON.parse(inp.dataset.assignedRuleNames) || []; } catch (e) { names = []; }
              }
              if (inp.dataset.assignedRulePatterns) {
                try { patterns = JSON.parse(inp.dataset.assignedRulePatterns) || []; } catch (e) { patterns = []; }
              }
              // append if not duplicate
              if (!names.includes(data.name)) {
                names.push(data.name || data.pattern || 'rule');
                patterns.push(data.pattern || '');
              }
              inp.dataset.assignedRuleNames = JSON.stringify(names);
              inp.dataset.assignedRulePatterns = JSON.stringify(patterns);
              inp.value = patterns.join(', ');

              // create or update badge for visual feedback (comma-separated names)
              let badge = fieldWrap.querySelector('.rc-assigned-badge');
              if (!badge) {
                badge = document.createElement('span');
                badge.className = 'rc-assigned-badge ml-3 px-2 py-1 rounded-full text-xs text-white';
                badge.style.background = '#1f2937';
                badge.style.cursor = 'default';
                fieldWrap.appendChild(badge);
              }
              badge.textContent = names.join(', ');
              // title: explanations joined by newline
              const explanations = names.map(n => { const found = RULES.find(rr => rr.name === n); return found ? (found.explanation || '') : ''; });
              badge.title = explanations.join('\n');
            } catch (e) {
              console.warn('Failed to assign rule on drop', e);
            }
          });

          // Also allow clicking the input label to clear assignment
          lbl.addEventListener('dblclick', (ev) => {
            ev.preventDefault();
            // clear all assigned rules
            inp.dataset.assignedRuleNames = '';
            inp.dataset.assignedRulePatterns = '';
            inp.value = '';
            const badge = fieldWrap.querySelector('.rc-assigned-badge');
            if (badge) badge.remove();
          });

          fieldWrap.appendChild(lbl);
          fieldWrap.appendChild(dash);
          fieldWrap.appendChild(inp);
          targetColumnsList.appendChild(fieldWrap);
        }

        // --- Create Rule Repository (palette) and place it to the RIGHT of the preview table ---
        try {
          const sidePreview = document.getElementById('side-excel-preview');
          const mainContentContainer = sidePreview || (sideContainer || modalContainer);
          if (mainContentContainer) {
            // create a wrapper that will hold the preview and the repo side-by-side
            let wrapper = document.getElementById('side-excel-wrapper');
            if (!wrapper) {
              wrapper = document.createElement('div');
              wrapper.id = 'side-excel-wrapper';
              // horizontal layout: preview (flex:1) + repo (fixed width)
              wrapper.style.display = 'flex';
              wrapper.style.alignItems = 'flex-start';
              wrapper.style.gap = '12px';
              wrapper.style.width = '100%';
            }

            // If sidePreview exists and is not already inside wrapper, move it into wrapper
            if (sidePreview && sidePreview.parentNode !== wrapper) {
              // ensure wrapper is inserted where sidePreview used to be
              const parent = sidePreview.parentNode;
              parent.insertBefore(wrapper, sidePreview);
              wrapper.appendChild(sidePreview);
            } else if (!sidePreview && (sideContainer || modalContainer) && wrapper.parentNode !== (sideContainer || modalContainer)) {
              // if we don't have a sidePreview element, ensure wrapper is attached to a sensible parent
              (sideContainer || modalContainer).appendChild(wrapper);
            } else if (sidePreview && sidePreview.parentNode === wrapper) {
              // already wrapped — ensure wrapper is in DOM
              if (!wrapper.parentNode) {
                const parent = sidePreview.parentNode;
                parent.appendChild(wrapper);
              }
            }

            // create or find the repo element and append it as the right-side child of wrapper
            let repo = document.getElementById('rule-repo');
            if (!repo) {
              repo = document.createElement('div');
              repo.id = 'rule-repo';
              repo.className = 'p-3 bg-white border border-slate-100 rounded-md shadow-sm';
              // fixed width so it appears as a vertical column to the right
              repo.style.width = '260px';
              repo.style.boxSizing = 'border-box';
              // ensure the repo aligns to the top of the preview (match any preview margin)
              repo.style.alignSelf = 'flex-start';
              try {
                if (sidePreview) {
                  const cs = window.getComputedStyle(sidePreview);
                  if (cs && cs.marginTop) repo.style.marginTop = cs.marginTop;
                } else {
                  repo.style.marginTop = '0';
                }
              } catch (e) { /* ignore */ }

              repo.innerHTML = `<h5 class="text-sm font-semibold text-slate-700 mb-2">Rule Repository</h5>
                <div id="rule-repo-list" class="flex flex-col gap-2 max-h-[60vh] overflow-auto"></div>
                <small class="text-xs text-slate-500 mt-2">Drag a rule and drop it onto a column input. Double-click the label to clear assignment.</small>`;
            }

            // append repo to wrapper if not present
            if (wrapper && repo.parentNode !== wrapper) wrapper.appendChild(repo);

            const repoList = repo.querySelector('#rule-repo-list');
            // Define the rules (can be extended later or loaded from backend)
            const RULES = [
              { name: 'Transaction ID (8 digits)', pattern: '^\\d{8}$', explanation: 'Transaction ID must be exactly 8 digits.' },
              { name: 'State Code (2 uppercase)', pattern: '^[A-Z]{2}$', explanation: 'Two uppercase letters (state code).' },
              { name: 'Process Date (MM/DD/YYYY)', pattern: '^(0[1-9]|1[0-2])\\/(0[1-9]|[12]\\d|3[01])\\/\\d{4}$', explanation: 'Process and delivery date in MM/DD/YYYY format.' },
              { name: 'Non-empty', pattern: '^(?!\\s*$).+', explanation: 'Value must not be empty or whitespace only.' }
            ];

            // clear existing (idempotent)
            repoList.innerHTML = '';
            RULES.forEach(r => {
              const el = document.createElement('div');
              el.className = 'rule-card p-2 rounded-md border border-slate-200 bg-slate-50 text-sm cursor-grab';
              el.draggable = true;
              el.dataset.pattern = r.pattern;
              el.dataset.name = r.name;
              el.dataset.explanation = r.explanation;
              el.style.display = 'flex';
              el.style.justifyContent = 'space-between';
              el.style.alignItems = 'center';
              el.style.padding = '6px';
              el.innerHTML = `<div style="flex:1;min-width:0"><strong style="display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${r.name}</strong><div style="font-size:11px;color:#475569">${r.explanation}</div></div><div style="margin-left:8px;color:#0ea5a4;font-weight:600">RegEx</div>`;

              el.addEventListener('dragstart', (ev) => {
                const payload = JSON.stringify({ name: r.name, pattern: r.pattern, explanation: r.explanation });
                try { ev.dataTransfer.setData('text/plain', payload); } catch (e) { /* some browsers */ }
              });

              // clicking rule appends pattern into currently focused input (if any)
              el.addEventListener('click', (ev) => {
                const active = document.activeElement;
                if (active && active.tagName === 'INPUT' && active.dataset && active.dataset.colIndex !== undefined) {
                  try {
                    // retrieve existing arrays from dataset (stored as JSON strings)
                    let names = [];
                    let patterns = [];
                    if (active.dataset.assignedRuleNames) {
                      try { names = JSON.parse(active.dataset.assignedRuleNames) || []; } catch (e) { names = []; }
                    }
                    if (active.dataset.assignedRulePatterns) {
                      try { patterns = JSON.parse(active.dataset.assignedRulePatterns) || []; } catch (e) { patterns = []; }
                    }
                    // avoid duplicates by name
                    if (!names.includes(r.name)) {
                      names.push(r.name);
                      patterns.push(r.pattern);
                    }
                    active.dataset.assignedRuleNames = JSON.stringify(names);
                    active.dataset.assignedRulePatterns = JSON.stringify(patterns);
                    active.value = patterns.join(', ');

                    // show/update badge with comma-separated names and explanations in title
                    const wrapper = active.closest('div');
                    if (wrapper) {
                      let badge = wrapper.querySelector('.rc-assigned-badge');
                      if (!badge) {
                        badge = document.createElement('span');
                        badge.className = 'rc-assigned-badge ml-3 px-2 py-1 rounded-full text-xs text-white';
                        badge.style.background = '#1f2937';
                        badge.style.cursor = 'default';
                        wrapper.appendChild(badge);
                      }
                      badge.textContent = names.join(', ');
                      // title: explanations joined by newline
                      const explanations = [];
                      names.forEach(n => {
                        const found = RULES.find(rr => rr.name === n);
                        if (found) explanations.push(found.explanation || '');
                      });
                      badge.title = explanations.join('\n');
                    }
                  } catch (ee) { console.warn('Append rule failed', ee); }
                }
              });

              repoList.appendChild(el);
            });
          }
        } catch (e) { console.warn('Failed to create rule repo', e); }
      }

      // Only show the preview container when there's table data to show.
      targetContainer.classList.remove('hidden');
    } catch (err) {
      console.warn('[excel-preview] render error', err);
    }
  }

  async function parseExcelAndShowPreview(file) {
    const res = await parseExcelFile(file);
    const rows = res && res.rows ? res.rows : [];
    renderExcelPreview(rows);
  }

  // Final confirmation handler — invoked when user presses the Upload (Confirm) button
  async function finalizeUploadAction() {
    if (!isTransferComplete || !stagedFile) {
      // If transfer didn't happen, warn user
      if (statusMessageEl) {
        statusMessageEl.textContent = 'No transferred file to confirm. Please select a file first.';
        statusMessageEl.classList.add('text-red-600');
      }
      return;
    }

    // Parse the Excel file client-side and show preview (first 5 rows)
    try {
      // parse rows
      const res = await parseExcelFile(stagedFile);
      const rows = res && res.rows ? res.rows : [];
      // hide the header upload button (remove it from UI now that user confirmed)
      try {
        const hdrUploadBtn = document.getElementById('state-upload-button');
        if (hdrUploadBtn) hdrUploadBtn.style.display = 'none';
      } catch (e) { /* ignore */ }

      // render preview into side area if present (renderExcelPreview prefers side)
      renderExcelPreview(rows);

      // optionally close modal preview if visible (we keep modal open behavior as-is, but hide modal preview)
      try {
        const modalPreview = document.getElementById('excel-preview');
        if (modalPreview) modalPreview.classList.add('hidden');
      } catch (e) { /* ignore */ }
    } catch (err) {
      console.warn('[excel-preview] failed to parse/show preview', err);
      if (statusMessageEl) {
        statusMessageEl.textContent = 'Upload confirmed, but preview unavailable.';
        statusMessageEl.classList.remove('text-red-600');
        statusMessageEl.classList.add('text-slate-600');
      }
    }

    // You can optionally POST to a finalization endpoint here if your backend needs it.
    // For now we simply mark finalized in UI and keep modal open so user can inspect preview.
    isFinalized = true;
    if (statusMessageEl) {
      statusMessageEl.textContent = '✅ Upload confirmed. File is ready.';
      statusMessageEl.classList.remove('text-red-600');
      statusMessageEl.classList.add('text-green-600');
    }

    // Change button label back to saved label or "Uploaded"
    const original = uploadButton.getAttribute('data-label') || 'Upload';
    uploadButton.textContent = original;
    // prevent re-confirmation
    uploadButton.disabled = true;
    uploadButton.classList.remove('bg-blue-600', 'hover:bg-blue-700', 'cursor-pointer');
    uploadButton.classList.add('bg-gray-400', 'cursor-not-allowed');

    // Optionally: call any UI refresh function so uploaded file appears in summary.
    try {
      if (typeof loadStates === 'function') loadStates();
      if (typeof renderSummaryPage === 'function') renderSummaryPage();
    } catch (e) {
      // ignore
    }

    // Close the upload modal now that user has confirmed and preview is visible on the right
    try {
      if (uploadModal) {
        uploadModal.classList.add('hidden');
        // clear dataset state
        try { delete uploadModal.dataset.forState; } catch (e) { /* ignore */ }
      }
    } catch (e) { /* ignore */ }
  }

  // wire events (idempotent)
  fileInput.removeEventListener('change', fileInput._rc_two_step_change_handler);
  fileInput._rc_two_step_change_handler = (e) => {
    const f = e.target.files && e.target.files[0];
    if (f) {
      showSelectedFile(f);
    }
  };
  fileInput.addEventListener('change', fileInput._rc_two_step_change_handler);

  if (overlay) {
    overlay.removeEventListener('change', overlay._rc_two_step_change_handler);
    overlay._rc_two_step_change_handler = (e) => {
      const f = e.target.files && e.target.files[0] || window.__rc_last_file_selected;
      if (f) showSelectedFile(f);
    };
    overlay.addEventListener('change', overlay._rc_two_step_change_handler);
  }

  uploadButton.removeEventListener('click', uploadButton._rc_two_step_click_handler);
  uploadButton._rc_two_step_click_handler = (e) => {
    e.preventDefault();
    if (isFinalized) {
      // already finalized - do nothing or close
      if (statusMessageEl) {
        statusMessageEl.textContent = 'Already confirmed.';
        statusMessageEl.classList.add('text-slate-600');
      }
      return;
    }
    if (!isTransferComplete) {
      // nothing staged — prompt user to select file (or wait for transfer)
      if (statusMessageEl) {
        statusMessageEl.textContent = 'Please select a file and wait for the transfer to complete before confirming.';
        statusMessageEl.classList.add('text-red-600');
      }
      return;
    }
    // finalize action (UI-only for now)
    finalizeUploadAction();
  };
  uploadButton.addEventListener('click', uploadButton._rc_two_step_click_handler);

  console.log('[2step-upload] Two-step upload flow active: select -> transfer -> Confirm Upload.');
})();

