// Function to get CSRF token from cookies
function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
  return null;
}

const OriginalXMLHttpRequest = window.XMLHttpRequest;
let allowPrimeSlots, startDate, startTime, endDate, endTime, dayOfWeek = null; // Initialize variables
let entitlementActive = false;
let availableSlots = null;
const UI_LANGUAGE_KEY = 'appointments_uiLanguage';
let uiLanguage = 'en';
let extensionContextValid = true;

function isExtensionContextError(error) {
  const message = String(error?.message || error || '');
  return message.includes('Extension context invalidated') ||
    message.includes('Receiving end does not exist') ||
    message.includes('Could not establish connection');
}

function markExtensionContextInvalid(error) {
  if (isExtensionContextError(error)) {
    extensionContextValid = false;
    console.debug('[OpenTLS] Extension context is no longer available:', error?.message || error);
    return true;
  }
  return false;
}

function hasExtensionContext() {
  return extensionContextValid &&
    typeof chrome !== 'undefined' &&
    Boolean(chrome.runtime?.id) &&
    Boolean(chrome.storage?.local);
}

function getRuntimeUrl(path) {
  try {
    if (!hasExtensionContext()) return null;
    return chrome.runtime.getURL(path);
  } catch (error) {
    markExtensionContextInvalid(error);
    return null;
  }
}

function storageGet(keys) {
  return new Promise((resolve) => {
    try {
      if (!hasExtensionContext()) {
        resolve({});
        return;
      }
      chrome.storage.local.get(keys, (result) => {
        const error = chrome.runtime.lastError;
        if (error) {
          markExtensionContextInvalid(error);
          resolve({});
          return;
        }
        resolve(result || {});
      });
    } catch (error) {
      markExtensionContextInvalid(error);
      resolve({});
    }
  });
}

function storageSet(data) {
  return new Promise((resolve) => {
    try {
      if (!hasExtensionContext()) {
        resolve(false);
        return;
      }
      chrome.storage.local.set(data, () => {
        const error = chrome.runtime.lastError;
        if (error) {
          markExtensionContextInvalid(error);
          console.debug('[OpenTLS] Storage write skipped:', error.message);
          resolve(false);
          return;
        }
        resolve(true);
      });
    } catch (error) {
      markExtensionContextInvalid(error);
      resolve(false);
    }
  });
}

function storageRemove(keys) {
  return new Promise((resolve) => {
    try {
      if (!hasExtensionContext()) {
        resolve(false);
        return;
      }
      chrome.storage.local.remove(keys, () => {
        const error = chrome.runtime.lastError;
        if (error) {
          markExtensionContextInvalid(error);
          resolve(false);
          return;
        }
        resolve(true);
      });
    } catch (error) {
      markExtensionContextInvalid(error);
      resolve(false);
    }
  });
}

function runtimeSendMessage(message) {
  return new Promise((resolve) => {
    try {
      if (!hasExtensionContext()) {
        resolve(null);
        return;
      }
      chrome.runtime.sendMessage(message, (response) => {
        const error = chrome.runtime.lastError;
        if (error) {
          markExtensionContextInvalid(error);
          resolve(null);
          return;
        }
        resolve(response || null);
      });
    } catch (error) {
      markExtensionContextInvalid(error);
      resolve(null);
    }
  });
}

function addRuntimeMessageListener(listener) {
  try {
    if (!hasExtensionContext()) return;
    chrome.runtime.onMessage.addListener(listener);
  } catch (error) {
    markExtensionContextInvalid(error);
  }
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

const CONTENT_COPY = {
  en: {
    bannerTitle: "OpenTLS is monitoring",
    simulatedSlotsWarning: "Do not click simulated slots manually. Keep this page open and let OpenTLS complete the booking flow.",
    simulatedSlotsLikelyWarning: "Do not click simulated slots manually. Manual clicks may trigger blocking; let OpenTLS continue automatically.",
    minimizeAllowedWarning: "Keep the computer and display awake while OpenTLS monitors. This window may be minimized on this page.",
    minimizeBlockedWarning: "This window CANNOT be minimized, and the computer and display CANNOT be in sleep mode.",
    workflowWarning: "Keep this window visible, keep the computer and display awake, and do not click simulated slots manually. Let OpenTLS book automatically, or stop monitoring before booking yourself.",
    slotBookingFailed: "Failed to book {datetime}. The slot may already be taken. OpenTLS will keep looking if monitoring is still running. If monitoring stopped, TLSContact may have returned a firewall warning.",
    applicantInformation: "Applicant Information",
    applicantNumber: "Applicant {number}",
    surname: "Surname",
    givenNames: "Given name(s)",
    dateOfBirth: "Date of birth",
    nationality: "Nationality",
    passportNo: "Passport No.",
    travelDate: "Travel date",
    dateOfArrival: "Arrival date",
    dateOfLeaving: "Leaving date",
    notAvailable: "N/A",
    applicantFooter: "Verify this information carefully. Some users reported profile changes after submission, and those changes can only be confirmed at the appointment.",
    dismiss: "Dismiss"
  },
  zh: {
    bannerTitle: "OpenTLS 正在监控",
    simulatedSlotsWarning: "请不要手动点击模拟出来的时段。保持页面打开，让 OpenTLS 自动完成预约流程。",
    simulatedSlotsLikelyWarning: "请不要手动点击模拟时段。手动点击可能触发限制，请让 OpenTLS 自动继续。",
    minimizeAllowedWarning: "OpenTLS 监控期间，请保持电脑和显示器唤醒。此页面可以最小化。",
    minimizeBlockedWarning: "此窗口不能最小化，电脑和显示器也不能进入睡眠状态。",
    workflowWarning: "请保持此窗口可见，并保持电脑和显示器唤醒。不要手动点击模拟时段；请让 OpenTLS 自动预约，或先停止监控再手动操作。",
    slotBookingFailed: "{datetime} 预约失败。该时段可能已被别人抢走。如果监控仍在运行，OpenTLS 会继续查找；如果监控已停止，可能是 TLSContact 返回了防火墙警告。",
    applicantInformation: "申请人信息",
    applicantNumber: "申请人 {number}",
    surname: "姓",
    givenNames: "名",
    dateOfBirth: "出生日期",
    nationality: "国籍",
    passportNo: "护照号",
    travelDate: "出行日期",
    dateOfArrival: "抵达日期",
    dateOfLeaving: "离开日期",
    notAvailable: "无",
    applicantFooter: "请仔细核对这些信息。有用户反馈提交后资料可能发生变化，最终只能在预约现场确认。",
    dismiss: "关闭"
  }
};

function formatContentText(template, values = {}) {
  return template.replace(/\{(\w+)\}/g, (_, name) =>
    values[name] === undefined || values[name] === null ? '' : String(values[name])
  );
}

function contentText(key, values = {}) {
  const template = CONTENT_COPY[uiLanguage]?.[key] || CONTENT_COPY.en[key] || key;
  return formatContentText(template, values);
}

async function loadUiLanguage() {
  const result = await storageGet(UI_LANGUAGE_KEY);
  uiLanguage = result[UI_LANGUAGE_KEY] === 'zh' ? 'zh' : 'en';
  return uiLanguage;
}

function injectStaticCacheInterceptor() {
  const scriptUrl = getRuntimeUrl('scripts/static-cache-interceptor.js');
  if (!scriptUrl) return;
  const script = document.createElement('script');
  script.src = scriptUrl;
  script.onload = function () {
    this.remove();
  };
  (document.head || document.documentElement).prepend(script);
  console.log('[Content Script] Injected static cache interceptor');
}

// Bridge for static-cache-interceptor to access chrome.storage
document.addEventListener('tls-storage-get', (e) => {
  const { keys, requestId } = e.detail;
  storageGet(keys).then((data) => {
    const responseEvent = new CustomEvent('tls-storage-response', {
      detail: { requestId, data, error: null }
    });
    document.dispatchEvent(responseEvent);
  });
});

document.addEventListener('tls-storage-set', (e) => {
  const { data } = e.detail;
  storageSet(data);
});

document.addEventListener('tls-storage-remove', (e) => {
  const { keys } = e.detail;
  storageRemove(keys);
});

// Inject as early as possible
injectStaticCacheInterceptor();


async function getCenterName() {
  // Services tab does not work for Egypt website. Clicking it keeps redirecting to the apppintment page.
  // Reset the appointment found flag to resume monitoring
  const html = document.documentElement.outerHTML;
  const parser = new DOMParser();
  const doc = parser.parseFromString(html, 'text/html');
  // Find the anchor tag containing "Welcome"
  const welcomeLink = Array.from(doc.querySelectorAll('a')).find(link =>
    link.textContent.includes('Welcome') || link.textContent.includes('Home')
  );
  let centerName = null;
  let countryCode = null;
  if (welcomeLink) {
    const href = welcomeLink.getAttribute('href');
    centerName = href.split('/').pop(); // Gets the last element
    console.log('Center name:', centerName); // Output: egHAC2de
  } else {
    console.log('Welcome link not found');
  }
  if (centerName) {
    countryCode = centerName.slice(0, 2).toLowerCase(); // Extract the first two characters as country code
  }
  // save centerName and countryCode to chrome.storage.local
  console.log('Saving centerName and countryCode to chrome.storage.local:', { centerName, countryCode });
  await storageSet({ centerName, countryCode });
}

async function getSettings() {
  await loadUiLanguage();
  await getCenterName(); // Ensure centerName is set before retrieving settings
  // 1. Retrieve Settings
  const settings = await runtimeSendMessage({ action: 'getAllowPrimeSlots' });

  if (!settings) {
    console.error('Could not retrieve settings from background script.');
    return; // Cannot proceed without settings
  }
  ({ allowPrimeSlots, startDate, startTime, endDate, endTime, dayOfWeek } = settings); // Added dayOfWeek
  console.log('Retrieved settings:', { allowPrimeSlots, startDate, startTime, endDate, endTime, dayOfWeek });
  entitlementActive = Boolean(settings.entitlementActive);
  console.log('Stored entitlement status:', entitlementActive);
}

// Function to inject a script into the page context
function injectScript(file_path) {
  const scriptUrl = getRuntimeUrl(file_path);
  if (!scriptUrl) return;
  const script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.setAttribute('metadata', 'tlscontactchromeextension');
  script.setAttribute('src', scriptUrl);
  (document.head || document.documentElement).appendChild(script);
  console.log(`Injected script: ${file_path}`);
}

// Function to send commands to the injected script
function sendCommandToPage(command) {
  // Use custom events to communicate with the injected script
  const event = new CustomEvent('tlsInterceptorCommand', {
    detail: { command: command }
  });
  document.dispatchEvent(event);
}

// Inject the interceptor script after the DOM is fully loaded
document.addEventListener('DOMContentLoaded', async () => {
  await getSettings(); // Fetch settings from background script
  const status = await getExtensionStatus();
  const isRunning = status.isRunning;
  console.log('Current window URL:', window.location.href, 'allowPrimeSlots:', allowPrimeSlots, 'entitlementActive:', entitlementActive, 'isRunning:', isRunning);
  if (window.location.href.includes('tlscontact.com/appointment/') && !window.location.href.includes('visas-be') && entitlementActive && isRunning) {
    injectScript('scripts/interceptor.js');
    // add below html element to div.tls-home-text-group
    // <h1 class="tls-heading-1">
    //             Don't click slots as they are simulated. If you do, you will get blocked. Leave it to the extension
    //         </h1>
    const warningElement = document.createElement('h1');
    warningElement.className = 'tls-heading-1';
    warningElement.textContent = contentText('simulatedSlotsWarning');
    let homeTextGroup = document.querySelector('div.tls-home-text-group');
    // check if homeTextGroup eists using while loop and wait for 1 second max 10 times
    let attempts = 0;
    while (!homeTextGroup && attempts < 10) {
      homeTextGroup = document.querySelector('div.tls-home-text-group');
      if (homeTextGroup) {
        console.log('Found tls-home-text-group, appending warning element');
        homeTextGroup.appendChild(warningElement);
      }
      attempts++;
      await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second
    }
    availableSlots = await scrapeTimeSlots();
    // check if availableSlots is not null using while loop and wait for 1 second max 10 times
    attempts = 0;
    while (Object.keys(availableSlots).length == 0 && attempts < 30) {
      console.log('Waiting for available slots to be scraped...');
      availableSlots = await scrapeTimeSlots();
      attempts++;
      await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second
    }
    console.log('Available slots scraped:', availableSlots);
    // delete html element
    document.querySelector('div.tls-appointment-time-picker')?.remove();
  } else if (window.location.href.includes('visas-fr.') && !window.location.href.includes('web.visas-fr.') && isRunning && entitlementActive) {
    console.log('Detected TLSContact France appointment page, injecting interceptor fr script');
    let monthButtons = []
    let attempts = 0;
    while (monthButtons.length < 1 && attempts < 30) {
      monthButtons = document.querySelectorAll('button[data-testid="btn-month-selector"]:not(:disabled)');
      attempts++;
      await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second
    }
    console.log(`Found ${monthButtons} available month buttons.`);
    console.log(`Found ${monthButtons.length} available month buttons.`);
    console.log(monthButtons);
    injectScript('scripts/interceptor-fr.js');
    await new Promise(resolve => setTimeout(resolve, 1000));
    monthButtons[1].click();
    const warningElement = document.createElement('h1');
    warningElement.className = 'text-2xl font-semibold text-primary-500';
    warningElement.textContent = contentText('simulatedSlotsLikelyWarning');
    let appointmentSelector = "#main > div.relative.flex-1.flex-1 > div > div.container.mx-auto.px-4.py-6.md\\:py-8.print\\:py-0 > div:nth-child(2) > div > div.mb-8.grid.grid-cols-1.items-start.gap-6.md\\:grid-cols-\\[minmax\\(0\\,_1fr\\)_17rem\\] > div.relative.col-span-2.rounded-lg.bg-white.shadow-primary.lg\\:col-span-1"
    let homeTextGroup = null;
    // check if homeTextGroup eists using while loop and wait for 1 second max 10 times
    attempts = 0;
    while (!homeTextGroup && attempts < 20) {
      console.log(`homeTextGroup: ${homeTextGroup}, attempts: ${attempts}`);
      homeTextGroup = document.querySelector(appointmentSelector);
      if (homeTextGroup) {
        console.log('Found tls-home-text-group, appending warning element');
        homeTextGroup.appendChild(warningElement);
      }
      attempts++;
      await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second
    }
    availableSlots = await scrapeTimeSlotsFr();
    // check if availableSlots is not null using while loop and wait for 1 second max 10 times
    attempts = 0;
    while (Object.keys(availableSlots).length == 0 && attempts < 10) {
      console.log('Waiting for available slots to be scraped...');
      availableSlots = await scrapeTimeSlotsFr();
      attempts++;
      await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second
    }
    console.log('Available slots scraped:', availableSlots);
    // delete html element

    document.querySelector("#main > div.relative.flex-1.flex-1 > div > div.container.mx-auto.px-4.py-6.md\\:py-8.print\\:py-0 > div:nth-child(2) > div > div.mb-8.grid.grid-cols-1.items-start.gap-6.md\\:grid-cols-\\[minmax\\(0\\,_1fr\\)_17rem\\] > div.relative.col-span-2.rounded-lg.bg-white.shadow-primary.lg\\:col-span-1 > div.px-4.pt-4.md\\:px-6 > div")?.remove();
    // availableSlots['2025-07-02']['07:30']['standard'].click();
  }
  // Remove loading overlay if it exists
  const loadingOverlay = document.querySelector('div.vld-overlay.is-active.is-full-page');
  if (loadingOverlay) {
    console.log('Found loading overlay, removing it to proceed with checking');
    loadingOverlay.remove();
  }
});


// Function to display the applicant data in the styled box
function displayApplicantData(applicantData, form_id) {
  console.log(`Displaying applicant data for form_id: ${form_id}`);
  if (!Array.isArray(applicantData) || applicantData.length === 0) {
    console.log('No applicant data provided or data is empty.');
    return;
  }

  // Remove existing container if it exists
  const existingContainer = document.getElementById('tls-applicant-info-container');
  if (existingContainer) {
    existingContainer.remove();
  }

  // Create main container
  const container = document.createElement('div');
  container.id = 'tls-applicant-info-container';
  container.style.position = 'fixed';
  container.style.top = '80px'; // Increased top margin
  container.style.right = '15px'; // Slightly more padding from edge
  container.style.width = '200px';
  container.style.maxHeight = 'calc(100vh - 100px)'; // Adjust max height based on top margin
  container.style.overflowY = 'auto';
  container.style.backgroundColor = '#f9f9f9'; // Slightly off-white background
  container.style.border = '1px solid #ddd'; // Lighter border
  container.style.borderRadius = '4px'; // Rounded corners
  container.style.padding = '15px';
  container.style.zIndex = '9999';
  container.style.boxShadow = '0 3px 8px rgba(0,0,0,0.15)'; // Softer shadow
  container.style.fontSize = '13px'; // Slightly larger base font
  container.style.lineHeight = '1.5'; // Better line spacing
  container.style.fontFamily = 'Arial, sans-serif'; // Consistent font

  // Add Close Button
  const closeButton = document.createElement('button');
  closeButton.textContent = '×'; // Multiplication sign for 'X'
  closeButton.title = contentText('dismiss');
  closeButton.style.position = 'absolute';
  closeButton.style.top = '5px';
  closeButton.style.right = '10px';
  closeButton.style.background = 'none';
  closeButton.style.border = 'none';
  closeButton.style.fontSize = '20px';
  closeButton.style.fontWeight = 'bold';
  closeButton.style.color = '#aaa';
  closeButton.style.cursor = 'pointer';
  closeButton.style.padding = '0 5px';
  closeButton.style.lineHeight = '1';
  closeButton.onmouseover = () => closeButton.style.color = '#333';
  closeButton.onmouseout = () => closeButton.style.color = '#aaa';
  closeButton.onclick = () => container.remove(); // Remove container on click
  container.appendChild(closeButton);


  const title = document.createElement('h4'); // Use h4 for less emphasis
  title.textContent = contentText('applicantInformation'); // Updated title text
  title.style.marginTop = '0px';
  title.style.marginBottom = '15px'; // More space below title
  title.style.paddingRight = '25px'; // Add padding to avoid overlap with close button
  title.style.borderBottom = '1px solid #ddd';
  title.style.paddingBottom = '8px';
  title.style.color = '#333';
  container.appendChild(title);

  // Loop through applicants
  applicantData.forEach((item, index) => {
    const applicant = item['data'];
    const applicantDiv = document.createElement('div');
    // Add border, padding, and margin for visual separation
    applicantDiv.style.border = '1px solid #ccc'; // Grey border around each applicant
    applicantDiv.style.borderRadius = '3px'; // Slightly rounded corners for the inner box
    applicantDiv.style.padding = '10px'; // Padding inside the border
    applicantDiv.style.marginBottom = '10px'; // Space between applicant boxes
    applicantDiv.style.backgroundColor = '#fff'; // White background for applicant box

    // Use spans for better potential styling later
    const notAvailable = contentText('notAvailable');
    applicantDiv.innerHTML = `
      <strong style="color: #555; display: block; margin-bottom: 5px;">${contentText('applicantNumber', { number: index + 1 })}</strong>
      <span><strong>${contentText('surname')}:</strong> ${applicant.f_pers_surnames || notAvailable}</span><br>
      <span><strong>${contentText('givenNames')}:</strong> ${applicant.f_pers_givennames || notAvailable}</span><br>
      <span><strong>${contentText('dateOfBirth')}:</strong> ${applicant.f_pers_birth_date || notAvailable}</span><br>
      <span><strong>${contentText('nationality')}:</strong> ${applicant.f_pers_nationality || notAvailable}</span><br>
      <span><strong>${contentText('passportNo')}:</strong> ${applicant.f_pass_num || notAvailable}</span><br>
      <span><strong>${contentText('travelDate')}:</strong> ${applicant.fi_trav_origin_departure_date || notAvailable}</span><br>
      <span><strong>${contentText('dateOfArrival')}:</strong> ${applicant.f_trav_departure_date || notAvailable}</span><br>
      <span><strong>${contentText('dateOfLeaving')}:</strong> ${applicant.f_trav_arrival_date || notAvailable}</span>
    `;
    // No need to remove border for last item anymore
    container.appendChild(applicantDiv);
  });

  // Add Footer/Attribution
  const footer = document.createElement('div');
  footer.style.marginTop = '15px';
  footer.style.paddingTop = '10px';
  footer.style.borderTop = '1px solid #ddd';
  footer.style.fontSize = '11px';
  footer.style.color = '#666';
  footer.style.fontStyle = 'italic';
  footer.textContent = contentText('applicantFooter');
  container.appendChild(footer);


  // Append container to body
  document.body.appendChild(container);
  console.log('Applicant data displayed on page.');
}


// Function to fetch and display applicant data, now with caching
async function fetchAndDisplayApplicantData() {
  console.log('Checking for applicant data...');
  try {
    const url = new URL(window.location.href);
    const domain = url.host; // Get the base domain
    const form_id = url.pathname.split('/')[1]
    const cacheKey = `applicantDataCache_${form_id}`;
    console.log('Parsed URL info:', { domain, form_id });
    console.log('Using cache key:', cacheKey);

    // 2. Check cache first
    const cached = await storageGet([cacheKey]);
    if (cached[cacheKey]) {
      console.log('Found cached applicant data for form_id:', form_id);
      displayApplicantData(cached[cacheKey], form_id);
      return;
    }

    // 3. If not in cache, proceed to fetch
    console.log('No cached data found for form_id:', form_id, '. Fetching from API...');

    const accessToken = localStorage.getItem('access_token');
    if (!accessToken) {
      console.error('Access token not found in localStorage');
      return;
    }

    const fetchUrl = `https://citizen.${domain}/v2/groups/${form_id}/onboarding/applicants`;
    const fetchHeaders = {
      "accept": "application/json, text/plain, */*",
      "authorization": `Bearer ${accessToken}`,
      "cache-control": "no-cache",
      "pragma": "no-cache"
    };

    console.log('Fetch URL:', fetchUrl);

    const response = await fetch(fetchUrl, {
      method: "GET",
      headers: fetchHeaders
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const fetchedData = await response.json();
    console.log('Received applicant data from API:', fetchedData);

    if (Array.isArray(fetchedData) && fetchedData.length > 0) {
      await storageSet({ [cacheKey]: fetchedData });
      displayApplicantData(fetchedData, form_id);
    } else {
      console.log('Fetched data is empty or not an array, not caching or displaying.');
    }

  } catch (error) {
    console.error('Error fetching or displaying applicant data:', error);
  }
}

async function actionsOnPageLoad() {
  if (window.location.href.includes('workflow/') && window.location.href.includes('visas-fr') && !window.location.href.includes('web.visas-fr')) {
    // Wait a bit for the page and cookies to be fully ready
    setTimeout(fetchAndDisplayApplicantData, 1500);
    // const centerName = appointmentInfo.center_name;
    const groupId = window.location.href.split('/')[3]
    const bookingsData = await checkBookingsFr(groupId);
    let bookedSlots = null;
    let center_name = null;
    if (!bookingsData || !bookingsData.body || !bookingsData.body.data) {
      // get centerName from chrome.storage.local
      const storageResult = await storageGet(['urlParamsForRules']);
      let urlParamsForRules = storageResult.urlParamsForRules;
      console.log('Retrieved urlParamsForRules from storage:', urlParamsForRules);
      center_name = urlParamsForRules ? urlParamsForRules['centerName'] : null;
      bookedSlots = {
        form_id: groupId,
        center_name: center_name || 'Unknown',
        slot_datetime: '2000-01-01 00:00',
        state_: bookingsData.status,
        labels: bookingsData
      };
      console.error('No bookings data found, using default bookedSlots:');
    } else {
      bookedSlots = {
        form_id: groupId,
        center_name: bookingsData.body.data.location,
        slot_datetime: `${bookingsData.body.date} ${bookingsData.body.time}`,
        state_: bookingsData.body.data.state,
        labels: bookingsData.body.data.labels,
      };
    }
    console.log('Booking data:', bookedSlots);
  } else {
    console.log('Not on the target tlscontact.com/appointment/ page, skipping applicant data fetch.');
  }
}

// --- Run Fetch Logic Only on Specific Appointment Pages ---
// document.addEventListener('DOMContentLoaded', actionsOnPageLoad);

let currentUrl = window.location.href;

async function checkForUrlChange() {
  if (window.location.href !== currentUrl) {
    currentUrl = window.location.href;
    // await actionsOnPageLoad();
  }
}

// Check every 500ms
setInterval(checkForUrlChange, 5000);


// --- Login Helper ---
function isOnLoginPage() {
  return window.location.href.includes("auth/realms/atlas/protocol/openid-connect") || (window.location.href.includes("tlscontact.com") && window.location.href.includes("login"));
}

async function retryGetElementById(selectorOrSelectors, retries = 15, delay = 1000) {
  const selectors = Array.isArray(selectorOrSelectors) ? selectorOrSelectors : [selectorOrSelectors];

  for (let attempt = 0; attempt < retries; attempt += 1) {
    for (const selector of selectors) {
      const element = document.getElementById(selector);
      if (element) {
        return element;
      }
    }
    await sleep(delay);
  }
  return null;
}

async function handleAutoLogin(email, password, redirectToUrl) {
  console.log('handleAutoLogin called. Email and redirectToUrl received.', email, redirectToUrl);
  if (!isOnLoginPage()) {
    console.log('Not on login page, autoLogin aborted.');
    return;
  }
  let usernameid, passwordid, loginid;
  if (window.location.href.includes('client_id=tlscitizen')) {
    console.log('US login page detected.');
    usernameid = 'email-input-field';
    passwordid = 'password-input-field';
    loginid = 'btn-login';

  } else {
    console.log('Non-US login page detected.');
    usernameid = ['username', 'email'];
    passwordid = 'password';
    loginid = ['kc-login', 'submit'];
  }
  const [usernameInput, passwordInput, loginButton] = await Promise.all([
    retryGetElementById(usernameid),
    retryGetElementById(passwordid),
    retryGetElementById(loginid)
  ]);
  if (usernameInput && passwordInput && loginButton) {
    // remove recaptcha div with id="it-recaptcha-here" if exists
    document.getElementById('it-recaptcha-here')?.remove();
    console.log('Login form elements found. Filling and submitting.');
    usernameInput.value = email;
    passwordInput.value = password;

    // Brief delay to ensure values are registered before click
    await sleep(200);
    loginButton.click();
  } else {
    console.error('Login form elements not found for auto-login.');
  }
}


// Global variables
let isMonitoring = false;
let appointmentFound = false;
let confirmAttempts = 0;
let firstPrimeSlot = null;

// Check if we're on a valid TLSContact appointment page
let appointment_page_url = null;
function isValidAppointmentPage() {
  console.log('Current url:', window.location.href);
  const flag = window.location.href.includes('tlscontact.com/appointment') || window.location.href.includes('workflow/appointment-booking');
  if (flag) {
    console.log(`Valid appointment page. Set appointment_page_url to: ${window.location.href}`);
    appointment_page_url = window.location.href;
  }
  return flag;
}

async function findAndClickAppointmentRouting() {
  if (window.location.href.includes('tlscontact.com/appointment')) {
    console.log('Detected TLSContact appointment page, calling findAndClickAppointment');
    const msg = contentText('minimizeAllowedWarning');
    showWarningBanner(msg);
    if (entitlementActive && !window.location.href.includes('visas-be')) {
      console.log('Detected TLSContact UK appointment page. Use different route');
      await new Promise(resolve => setTimeout(resolve, 8000));
      const appointmentInfo = await extractAppointmentInfo();
      const centerName = appointmentInfo.center_name;
      const groupId = appointmentInfo.appointment_id;
      console.log(`Center Name: ${centerName}, Group ID: ${groupId}`);
      bookAppointment(centerName, groupId, "standard", availableSlots)
      console.log('Search for standard appointment slots every 60 seconds.');
      setInterval(() => bookAppointment(centerName, groupId, "standard", availableSlots), 60000);
      // if allowPrimeSlots is true
      if (allowPrimeSlots) {
        // wait for 20 seconds 
        await new Promise(resolve => setTimeout(resolve, 20000));
        console.log('Search for prime appointment slots every 60 seconds.');
        bookAppointment(centerName, groupId, "prime", availableSlots)
        setInterval(() => bookAppointment(centerName, groupId, "prime", availableSlots), 60000);
      }
      return;
    }
    await findAndClickAppointment();
  }
  else if (window.location.href.includes('workflow/appointment-booking') && (window.location.href.includes('visas-de') || window.location.href.includes('visas-fr') || window.location.href.includes('visas-it'))) {
    const msg = contentText('minimizeBlockedWarning');
    showWarningBanner(msg);
    await findAndClickAppointmentDE();
  }
  else if (window.location.href.includes('workflow/appointment-booking')) {
    console.log('Detected TLSContact USA appointment page, calling findAndClickAppointmentUSA');
    const msg = contentText('workflowWarning');
    showWarningBanner(msg);
    if (entitlementActive) {
      console.log('Detected TLSContact France appointment page.');
      await new Promise(resolve => setTimeout(resolve, 8000));
      const appointmentInfo = await extractAppointmentInfo();
      const centerName = appointmentInfo.center_name;
      const groupId = appointmentInfo.appointment_id;
      console.log(`Center Name: ${centerName}, Group ID: ${groupId}`);
      bookAppointmentFr(centerName, groupId, availableSlots)
      console.log('Search for standard appointment slots every 60 seconds.');
      setInterval(() => bookAppointmentFr(centerName, groupId, availableSlots), 30000);
      return;
    }
    await findAndClickAppointmentUSA();
  } else {
    console.log('Not on a recognized appointment page, stopping monitoring');
  }
}

const monthMapGlobal = {
  January: '01', February: '02', March: '03', April: '04', May: '05', June: '06',
  July: '07', August: '08', September: '09', October: '10', November: '11', December: '12'
};

// Helper function to parse datetime from aria-label (USA format)
function parseDateTimeFromLabel(ariaLabel) {
  if (!ariaLabel) return null;

  // Example: "Thursday, May 8, 2025 at 07:30 AM, Standard appointment, Premium lounge unavailable"
  const dateTimeRegex = /(\w+),\s(\w+)\s(\d{1,2}),\s(\d{4})\s+at\s+(\d{1,2}):(\d{2})\s+(AM|PM)/;
  const match = ariaLabel.match(dateTimeRegex);

  if (!match) {
    console.error('Could not parse dateTime from aria-label:', ariaLabel);
    return null;
  }

  const [, , monthName, day, year, hourStr, minute, ampm] = match;
  const month = monthMapGlobal[monthName];
  if (!month) {
    console.error('Invalid month name:', monthName);
    return null;
  }

  const dayFormatted = day.padStart(2, '0');

  let hour = parseInt(hourStr, 10);
  if (ampm === 'PM' && hour !== 12) {
    hour += 12;
  } else if (ampm === 'AM' && hour === 12) {
    hour = 0; // Midnight case
  }
  const hourFormatted = String(hour).padStart(2, '0');

  return `${year}-${month}-${dayFormatted} ${hourFormatted}:${minute}`; // YYYY-MM-DD HH:MM
}

// Helper function to determine slot type from aria-label (USA format)
function getSlotTypeFromLabel(ariaLabel) {
  if (!ariaLabel) return 'unknown';
  // if (ariaLabel.includes('Prime Time appointment')) return 'prime';
  // if (ariaLabel.includes('Standard appointment')) return 'standard';
  // first lower case the ariaLabel and then check if it contains words 'prime' or 'premium', then return 'prime' otherwise return 'standard'
  const labelLower = ariaLabel.toLowerCase();
  if (labelLower.includes('prime') || labelLower.includes('premium')) {
    return 'prime';
  } else {
    return 'standard';
  }
}


// retry document.querySelector for 10 times and wait 1 second each time, return the element or null
async function asyncRetryQuerySelector(selector, retries = 10, delay = 1000) {
  let attempts = 0;
  let element = null;

  while (attempts < retries) {
    element = document.querySelector(selector);
    if (element) {
      return element;
    }
    attempts++;
    await new Promise(resolve => setTimeout(resolve, delay));
  }
  return null; // Return null if not found after all retries
}

// retry document.querySelector for 10 times and wait 1 second each time, return the element or null
function retryQuerySelector(selector, retries = 15, delay = 2000) {
  let attempts = 0;
  let element = null;

  while (attempts < retries) {
    element = document.querySelector(selector);
    if (element) {
      return element;
    }
    attempts++;
    // console.log(`Pause for ${delay / 1000} second`);
    setTimeout(() => { }, delay);
  }
  return null; // Return null if not found after all retries
}


let appointmentTypeDE = null;
let appointmentDateTimeDE = null;
let lastCallTimeDE = 0;


function replaceMonthSelector() {
  // Find the existing element
  const existingElement = document.querySelector('p[data-testid="btn-next-month-unavailable"]');

  if (!existingElement) {
    console.warn('Element with data-testid "btn-next-month-unavailable" not found');
    return;
  }

  // Calculate current month + 3 months
  const currentDate = new Date();
  const targetDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 3, 1);

  // Get month name and format date for URL
  const monthNames = [
    'January', 'February', 'March', 'April', 'May', 'June',
    'July', 'August', 'September', 'October', 'November', 'December'
  ];

  const monthName = monthNames[targetDate.getMonth()];
  const year = targetDate.getFullYear();
  const monthNumber = (targetDate.getMonth() + 1).toString().padStart(2, '0');
  const urlMonth = `${monthNumber}-${year}`;
  const displayText = `${monthName} ${year}`;
  const groupdId = window.location.href.split('/')[4];
  // Create the new anchor element
  const newElement = document.createElement('a');
  newElement.setAttribute('data-testid', 'btn-next-month-available');
  newElement.className = 'MonthSelector_month-selector_button__An0eF MonthSelector_--active__K1ooB';
  newElement.href = `/en-us/${groupdId}/workflow/appointment-booking?month=${urlMonth}`;
  newElement.textContent = displayText;

  // Replace the existing element
  existingElement.parentNode.replaceChild(newElement, existingElement);

  console.log(`Replaced month selector with: ${displayText} (${urlMonth})`);
}


async function findAndClickAppointmentDE() {
  try {
    if (Date.now() - lastCallTimeDE < 5000) {
      console.log('findAndClickAppointmentDE called within 5 seconds. Skipping.');
      return;
    }
    lastCallTimeDE = Date.now();

    const selectedMonths = getSelectedMonths();
    console.log('Selected Months:', selectedMonths);

    // wait for 2 seconds
    // await new Promise(resolve => setTimeout(resolve, 4000));
    // 2. Find Month Buttons
    let monthButtons = ['[data-testid="btn-current-month-available"]', '[data-testid="btn-next-month-available"]']
    const linksWithEG = document.querySelectorAll('a[href*="eg/"]');
    if (linksWithEG.length > 0) {
      monthButtons.push('[data-testid="btn-next-month-available"]', '[data-testid="btn-next-month-available"]');
    }
    console.log(`Found ${monthButtons.length} available month buttons.`);
    if (monthButtons.length <= 1) {
      console.log('Only one or zero month buttons available, checking current month only.');
      // If only the current month is available, we don't need to click anything to start
    }
    const slotButtonSelectors = [
      'button[data-testid^="btn-available-slot"]:not(:disabled)',  // Matches any variant starting with this
      'button[class*="AppointmentHour_appointment-hour"]:not(:disabled)', // Class-based
      'button[class*="appointment-hour"]:not(:disabled)',          // More general class-based
    ];

    // Function to check slots for the currently displayed month
    const checkCurrentMonthSlots = (monthName) => {
      let availableSlots = []; // Reset for each month check
      // Define the static part of the class name you are looking for
      const classNamePrefix = "group AppointmentDay_appointment-day__";
      // Construct the CSS selector: select <div> elements where the 'class' attribute
      // starts with the defined prefix.
      const selector = `div[class^="${classNamePrefix}"]`;
      // Use document.querySelectorAll to find all elements matching the selector
      const allDates = document.querySelectorAll(selector);
      allDates.forEach(dateElement => {
        const dateLabel = dateElement.querySelector('span.font-bold').textContent;
        let timeSlotButtons = null;
        let usedSelector = null;

        for (const selector of slotButtonSelectors) {
          try {
            timeSlotButtons = dateElement.querySelectorAll(selector);
            if (timeSlotButtons.length > 0) {
              usedSelector = selector;
              break;
            }
          } catch (e) {
            console.warn(`Button selector failed: ${selector}`, e);
          }
        }
        console.log(`Found ${timeSlotButtons.length} available time slots for the current month view.`);

        timeSlotButtons.forEach(button => {
          // dateTimeString is in format "YYYY-MM-DD HH:MM" constructed from monthName (e.g., "July 2025"), dateLable (e.g., 01) and button.childNodes[1] (e.g., 07:30)
          const month = monthMapGlobal[monthName.split(' ')[0]];
          const year = monthName.split(' ')[1];

          const dateTimeString = `${year}-${month}-${dateLabel} ${button.childNodes[1].textContent}`;
          const slotType = getSlotTypeFromLabel(button.querySelector('span.sr-only').textContent);

          if (dateTimeString) {
            availableSlots.push({
              buttonElement: button,
              dateTimeString: dateTimeString,
              slotType: slotType
            });
            // console.log(`Added slot: ${dateTimeString}, Type: ${slotType}`);
          } else {
            console.warn('Could not parse datetime for a button, skipping:', ariaLabel);
          }
        });
      });
      return availableSlots;
    };


    // 4. Iterate Through Other Months (skip the first one as it's already loaded/checked)
    for (let i = 0; i < monthButtons.length; i++) {
      let monthButton = retryQuerySelector(monthButtons[i]);
      if (!monthButton && linksWithEG.length > 0 && i == monthButtons.length - 1) {
        console.log('Enable the last month button again as it contains eg/');
        replaceMonthSelector();
        monthButton = retryQuerySelector(monthButtons[i]);
      }
      if (!monthButton) {
        console.warn(`Month button not found for index ${i}, ${monthButtons[i]}, stopping further checks.`);
        break; // Stop checking if no more month buttons are available
      }
      const monthName = monthButton.textContent;
      if (selectedMonths && !selectedMonths.includes(monthName)) {
        console.log(`Skipping ${monthName} as it is not in the selected months list.`);
        continue;
      }
      if (i > 0) {
        console.log(`Clicking month button: ${monthName}`);
        monthButton.click();
        await new Promise(resolve => setTimeout(resolve, 3000)); // Wait for slots to load
      }
      console.log(`Checking slots for month: ${monthName}`);
      let availableSlots = checkCurrentMonthSlots(monthName); // Check the slots for the newly loaded month
      console.log(`Found ${availableSlots.length} available slots for month: ${monthName}`);
      if (availableSlots.length == 0) {
        continue;
      } else {
        const booked = await bookSlot(availableSlots, monthName);
        if (booked) {
          console.log('Booking process initiated, stopping further checks.');
          break; // Stop checking if a slot was booked
        }
      }

    }

    async function bookSlot(availableSlots, monthName) {
      console.log(`Collected a total of ${availableSlots.length} available slots across all checked months.`);

      // 5. Filter Slots
      const filteredSlots = availableSlots.filter(slot =>
        isWithinDateAndTimeRange(slot.dateTimeString, startDate, startTime, endDate, endTime, dayOfWeek)
      );
      console.log(`Found ${filteredSlots.length} slots within the specified date/time range and day of week.`);

      if (filteredSlots.length === 0) {
        console.log(`No suitable appointment slots found after filtering for month: ${monthName}`);
        return;
      }

      // 6. Build ordered candidate list: standard slots first (sorted by date descending), then prime slots (if allowed)
      // We sort descending (latest first) to reduce conflict with other users who typically compete for the earliest slots
      filteredSlots.sort((a, b) => b.dateTimeString.localeCompare(a.dateTimeString));

      const orderedCandidates = [];
      // Add all standard slots first
      orderedCandidates.push(...filteredSlots.filter(slot => slot.slotType === 'standard'));
      // Then add prime slots if allowed
      if (allowPrimeSlots) {
        orderedCandidates.push(...filteredSlots.filter(slot => slot.slotType === 'prime'));
      }

      if (orderedCandidates.length === 0) {
        console.log('No eligible slots found (standard slots unavailable, prime slots not allowed or unavailable).');
        return;
      }

      console.log(`Will try up to ${orderedCandidates.length} candidate slots in order.`);

      // 7. Try each candidate slot sequentially until one succeeds (page navigates away)
      for (let idx = 0; idx < orderedCandidates.length; idx++) {
        const candidateSlot = orderedCandidates[idx];
        console.log(`[Attempt ${idx + 1}/${orderedCandidates.length}] Trying slot: ${candidateSlot.dateTimeString} (${candidateSlot.slotType})`);

        // Click the slot button to select it
        candidateSlot.buttonElement.click();

        // Poll for the confirm button (retry every 500ms, up to 3s)
        let confirmButton1 = null;
        for (let retry = 0; retry < 6; retry++) {
          await new Promise(resolve => setTimeout(resolve, 500));
          const allconfirmButton1 = document.querySelectorAll('button[type="submit"]');
          confirmButton1 = allconfirmButton1.length > 1 ? allconfirmButton1[1] : allconfirmButton1[0];
          if (confirmButton1) break;
        }

        if (!confirmButton1) {
          console.warn(`[Attempt ${idx + 1}] Confirm button not found after clicking slot. Trying next slot...`);
          continue;
        }

        // Store info before clicking confirm, in case the page navigates away
        appointmentTypeDE = candidateSlot.slotType;
        appointmentDateTimeDE = candidateSlot.dateTimeString;
        storeAppointmentSlot(candidateSlot.slotType, candidateSlot.dateTimeString, true, null, null);
        console.log(`[Attempt ${idx + 1}] Clicking first confirm button...`);
        confirmButton1.click();

        // Wait and check if the page navigated to the order-summary page
        // If the slot was already taken, the page stays on the appointment-booking page
        const navigationCheckDelayMs = 2000;
        console.log(`[Attempt ${idx + 1}] Waiting ${navigationCheckDelayMs / 1000}s to check if page navigated...`);
        await new Promise(resolve => setTimeout(resolve, navigationCheckDelayMs));

        // If the page has navigated away (to order-summary or any other page), we succeeded
        const currentUrl = window.location.href;
        if (!currentUrl.includes('appointment-booking')) {
          console.log(`[Attempt ${idx + 1}] SUCCESS - Page navigated away to: ${currentUrl}`);
          confirm_btn1_flag = true;
          return true;
        }

        // Page did NOT navigate - the slot was likely taken by someone else
        console.warn(`[Attempt ${idx + 1}] Slot ${candidateSlot.dateTimeString} appears to be taken (page did not navigate). Trying next slot...`);
      }

      // All candidates exhausted without success
      console.log(`All ${orderedCandidates.length} candidate slots were tried but none succeeded for month: ${monthName}`);
      return false;
    }

  } catch (error) {
    console.error('Error during findAndClickAppointmentDE:', error);
    // Notify background script of failure
    notifyAppointmentStatus(false, { reason: 'exception_in_findAndClickAppointmentDE' });
    appointmentFound = false; // Reset flag to allow retries if applicable
  }
}

function getSelectedMonths() {
  if (!startDate || !endDate) {
    console.log('startDate or endDate is missing. Returning null (all months allowed).');
    return null;
  }
  const start = new Date(startDate);
  const end = new Date(endDate);
  const months = [];

  // Set to the first day of the month to avoid edge case skipping
  let current = new Date(start.getFullYear(), start.getMonth(), 1);
  const limit = new Date(end.getFullYear(), end.getMonth(), 1);

  while (current <= limit) {
    const monthName = current.toLocaleString('en-US', { month: 'long' });
    const year = current.getFullYear();
    months.push(`${monthName} ${year}`);
    current.setMonth(current.getMonth() + 1);
  }
  return months;
}


async function findAndClickAppointmentUSA() {
  console.log('findAndClickAppointmentUSA called');

  // Only proceed if we're on a valid appointment page
  if (!isValidAppointmentPage()) {
    console.log('Not on a valid TLSContact USA appointment page, stopping monitoring');
    isMonitoring = false;
    runtimeSendMessage({ action: 'appointmentPageInvalid' });
    return;
  }

  try {
    // wait for 2 seconds
    await new Promise(resolve => setTimeout(resolve, 4000));
    // 2. Find Month Buttons
    const monthButtons = document.querySelectorAll('button[data-testid="btn-month-selector"]:not(:disabled)');
    console.log(`Found ${monthButtons.length} available month buttons.`);
    if (monthButtons.length <= 1) {
      console.log('Only one or zero month buttons available, checking current month only.');
      // If only the current month is available, we don't need to click anything to start
    }

    // Function to check slots for the currently displayed month
    const checkCurrentMonthSlots = () => {
      let availableSlots = []; // Reset for each month check
      const timeSlotButtons = document.querySelectorAll('button[data-testid="btn-add-appointment-slot"]:not(:disabled)');
      console.log(`Found ${timeSlotButtons.length} available time slots for the current month view.`);

      timeSlotButtons.forEach(button => {
        const ariaLabel = button.getAttribute('aria-label');
        const dateTimeString = parseDateTimeFromLabel(ariaLabel);
        const slotType = getSlotTypeFromLabel(ariaLabel);

        if (dateTimeString) {
          availableSlots.push({
            buttonElement: button,
            dateTimeString: dateTimeString,
            slotType: slotType
          });
          // console.log(`Added slot: ${dateTimeString}, Type: ${slotType}`);
        } else {
          console.warn('Could not parse datetime for a button, skipping:', ariaLabel);
        }
      });
      return availableSlots;
    };


    // 4. Iterate Through Other Months (skip the first one as it's already loaded/checked)
    for (let i = 0; i < 2; i++) {
      const monthButton = monthButtons[i];
      const monthName = monthButton.textContent || `Month ${i + 1}`;
      console.log(`Clicking month button: ${monthName}`);
      if (i > 0) {
        monthButton.click();
      }
      await new Promise(resolve => setTimeout(resolve, 2000)); // Wait for slots to load

      console.log(`Checking slots for month: ${monthName}`);
      let availableSlots = checkCurrentMonthSlots(); // Check the slots for the newly loaded month
      console.log(`Found ${availableSlots.length} available slots for month: ${monthName}`);
      if (availableSlots.length == 0) {
        continue;
      } else {
        const booked = await bookSlot(availableSlots, monthName);
        if (booked) {
          console.log('Booking process initiated, stopping further checks.');
          break; // Stop checking if a slot was booked
        }
      }

    }

    async function bookSlot(availableSlots, monthName) {
      console.log(`Collected a total of ${availableSlots.length} available slots across all checked months.`);

      // 5. Filter Slots
      const filteredSlots = availableSlots.filter(slot =>
        isWithinDateAndTimeRange(slot.dateTimeString, startDate, startTime, endDate, endTime, dayOfWeek) // Added dayOfWeek
      );
      console.log(`Found ${filteredSlots.length} slots within the specified date/time range and day of week.`);

      if (filteredSlots.length === 0) {
        console.log(`No suitable appointment slots found after filtering for month: ${monthName}`);
        // Optionally navigate back to the first month?
        // if (monthButtons.length > 0) monthButtons[0].click();
        return;
      }

      // 6. Select Target Slot
      // Sort by date/time ascending
      filteredSlots.sort((a, b) => a.dateTimeString.localeCompare(b.dateTimeString));

      let targetSlot = filteredSlots.find(slot => slot.slotType === 'standard');
      console.log(`Earliest standard slot found: ${targetSlot ? targetSlot.dateTimeString : 'None'}`);

      if (!targetSlot && allowPrimeSlots) {
        targetSlot = filteredSlots.find(slot => slot.slotType === 'prime');
        console.log(`No standard slot found. Earliest prime slot found: ${targetSlot ? targetSlot.dateTimeString : 'None'}`);
      } else if (!targetSlot) {
        console.log('No standard slot found, and prime slots are not allowed.');
      }

      // 7. Execute Booking
      if (targetSlot) {
        console.log(`Attempting to book target slot: ${targetSlot.dateTimeString} (${targetSlot.slotType})`);
        targetSlot.buttonElement.click();
        let confirm_btn1_flag = false;
        let confirm_btn2_flag = false;
        let confirm_btn3_flag = false;
        const confirmButton1 = await asyncRetryQuerySelector('button[data-testid="btn-basket-confirm"]');
        if (confirmButton1) {
          console.log('Clicking first confirm button...');
          // Extract details from aria-label if needed, though we already have targetSlot info
          // const confirmLabel = confirmButton1.getAttribute('aria-label');
          confirmButton1.click();
          confirm_btn1_flag = true;
          // Click second confirm button (often the same selector after modal update/navigation)
          await new Promise(resolve => setTimeout(resolve, 3000));
          const confirmButton2 = await asyncRetryQuerySelector('button[data-testid="btn-basket-confirm"]');
          if (confirmButton2) {
            // delay for 4 seconds
            console.log('Clicking second confirm button...');
            console.log(confirmButton2)
            confirmButton2.click();
            confirm_btn2_flag = true;
            // Click third confirm button (Proceed to checkout)
            await new Promise(resolve => setTimeout(resolve, 3000));
            const confirmButton3 = await asyncRetryQuerySelector('button[data-testid="btn-go-to-payment"]');
            if (confirmButton3) {
              console.log('Clicking third confirm button (Proceed to checkout)...');
              confirmButton3.click();
              confirm_btn3_flag = true;
              console.log('Appointment booking process fully initiated!');
              notifyAppointmentStatus(true, { datetime: targetSlot.dateTimeString, slotType: targetSlot.slotType });
              notifyAppointmentFound(targetSlot.slotType); // Set flag to stop monitoring loop
              appointmentFound = true; // Set flag to stop monitoring loop
              return true;
            } else {
              console.error('Third confirm button (data-testid="btn-go-to-payment") not found.');
              // Notify failure? Or maybe the process completed differently?
              // Consider if this is a critical failure or an alternative path.
              notifyAppointmentStatus(false, { reason: 'third_confirm_button_not_found' });
              appointmentFound = false; // Re-enable monitoring if this is a failure
            }
          } else {
            console.error('Second confirm button (data-testid="btn-basket-confirm") not found after first click.');
            // Notify failure? Or maybe the process completed? Depends on workflow.
            notifyAppointmentStatus(false, { reason: 'second_confirm_button_not_found' });
            appointmentFound = false; // Allow monitoring to continue if needed
          }
        } else {
          console.error('First confirm button (data-testid="btn-basket-confirm") not found after clicking slot.');
          notifyAppointmentStatus(false, { reason: 'first_confirm_button_not_found' });
          appointmentFound = false; // Allow monitoring to continue if needed
        }
        storeAppointmentSlot(targetSlot.slotType, targetSlot.dateTimeString, confirm_btn1_flag, confirm_btn2_flag, confirm_btn3_flag);
      } else {
        console.log('No target slot selected (either none found within range/preference or only unknown types).');
        // Optionally navigate back to the first month?
        // if (monthButtons.length > 0) monthButtons[0].click();
      }
    }

  } catch (error) {
    console.error('Error during findAndClickAppointmentUSA:', error);
    // Notify background script of failure
    notifyAppointmentStatus(false, { reason: 'exception_in_findAndClickAppointmentUSA' });
    appointmentFound = false; // Reset flag to allow retries if applicable
  }
}

// Function to find and click on non-prime time appointment
async function findAndClickAppointment() {
  console.log('find And Click Appointment called');

  // Only proceed if we're on a valid appointment page
  if (!isValidAppointmentPage()) {
    console.log('Not on a valid TLSContact appointment page, stopping monitoring');
    isMonitoring = false;
    // Notify background that we're not on a valid page
    runtimeSendMessage({
      action: 'appointmentPageInvalid'
    });
    return;
  }

  let pagesChecked = 0;
  const MAX_PAGES = 30; // Safety limit to prevent infinite loops
  let onFirstPage = true;
  let retry = false;
  firstPrimeSlot = null;

  // Start checking the current page
  console.log('Starting to check the current page for appointments');
  checkCurrentPage();

  function checkCurrentPage() {
    pagesChecked++;
    setTimeout(() => {
      console.log(`Checking page ${pagesChecked} for appointment slots`);
    }, 2000);

    // Find standard slots on current page
    const standardSlots = document.querySelectorAll('button.tls-time-unit.-available:not(.-prime)');
    console.log(`Found ${standardSlots.length} standard slots on page ${pagesChecked}`);
    // If this is the first page, save the first prime slot we find as a fallback
    if (onFirstPage) {
      const primeSlots = document.querySelectorAll('button.tls-time-unit.-available.-prime');
      if (primeSlots.length > 0 && !firstPrimeSlot) {
        firstPrimeSlot = primeSlots[0];
        console.log('Saved first prime slot as fallback');
      } else {
        console.log('No prime slots found on first page');
      }
      onFirstPage = false;
    }
    // If we found a standard slot, book it immediately
    if (standardSlots.length > 0) {
      console.log('Found standard slot - booking immediately');
      // Click the first available standard slot
      standardSlots[0].click();
      console.log('Clicked on standard appointment slot');
      // Wait for confirmation popup and confirm
      setTimeout(() => confirmAppointment('standard', null), 500);
      return;
    } else if (!firstPrimeSlot && !retry) {
      console.log('No standard slots found, try again in case page not fully loaded');
      retry = true;
      setTimeout(() => checkCurrentPage(), 5000);
      return;
    }

    // Check if we've hit our safety limit
    if (pagesChecked >= MAX_PAGES) {
      console.log(`Reached maximum page check limit (${MAX_PAGES})`);
      bookFallbackSlot();
      return;
    }

    // Check if there's a next page button
    const nextPageButton = document.querySelector('button.tls-time-picker--arrow svg[data-icon="chevron-right"]');

    if (nextPageButton) {
      console.log('Found next page button, navigating to next page');
      // Get the parent button
      const buttonElement = nextPageButton.closest('button');

      try {
        // Try both click methods for better compatibility
        buttonElement.dispatchEvent(new MouseEvent('click', {
          bubbles: true,
          cancelable: true,
          view: window
        }));

        // Add a more reliable way to detect if the page has changed
        const currentSlots = document.querySelectorAll('button.tls-time-unit').length;

        setTimeout(() => {
          const newSlots = document.querySelectorAll('button.tls-time-unit').length;
          // If the number of slots hasn't changed, we probably haven't moved to a new page
          if (newSlots === currentSlots) {
            console.log('Page did not change after clicking next, assuming no more pages');
            bookFallbackSlot();
          } else {
            checkCurrentPage();
          }
        }, 1000);
      } catch (e) {
        console.error('Error clicking next page button:', e);
        bookFallbackSlot();
      }
    } else {
      console.log('No more pages to navigate');
      bookFallbackSlot();
    }
  }

  function bookFallbackSlot() {
    if (firstPrimeSlot) {
      console.log('No standard slots found after checking all pages, booking prime slot as fallback');
      // Click the first prime slot we saved from the first page
      firstPrimeSlot.click();
      console.log('Clicked on prime time appointment slot');

      // Wait for confirmation popup and confirm
      setTimeout(() => confirmAppointment('prime', null), 500);
    } else {
      console.log('No appointment slots found on any page');
      appointmentFound = false;

      // Try to navigate back to the first page
      const firstPageButton = document.querySelector('.tls-pagination--item:first-child');
      if (firstPageButton) {
        firstPageButton.click();
      }
    }
  }
}

// Function to check if a datetime string (YYYY-MM-DD HH:MM) falls within separate date and time ranges
function isWithinDateAndTimeRange(dateTimeString, startDateStr, startTimeStr, endDateStr, endTimeStr, dayOfWeekFilter) {
  if (!dateTimeString) return false; // Cannot check without a valid appointment datetime

  // If no range is specified at all (excluding dayOfWeekFilter for this initial check), it's valid for date/time
  // dayOfWeekFilter is handled separately.
  // if (!startDateStr && !startTimeStr && !endDateStr && !endTimeStr) {
  //   return true; // This part is too broad, let's refine
  // }

  try {
    const appointmentFullDate = new Date(dateTimeString.replace(' ', 'T')); // Ensure ISO format for Date constructor
    const appointmentDateOnlyStr = dateTimeString.substring(0, 10);
    const appointmentTimeOnlyStr = dateTimeString.substring(11, 16);

    // console.log(`Checking slot: ${dateTimeString}. Filters: StartDate: ${startDateStr}, EndDate: ${endDateStr}, StartTime: ${startTimeStr}, EndTime: ${endTimeStr}, DayOfWeek: ${dayOfWeekFilter}`);

    // --- Date Range Check ---
    if (startDateStr && appointmentDateOnlyStr < startDateStr) {
      // console.log(`Slot ${dateTimeString} is before start date ${startDateStr}.`);
      return false;
    }
    if (endDateStr && appointmentDateOnlyStr > endDateStr) {
      // console.log(`Slot ${dateTimeString} is after end date ${endDateStr}.`);
      return false;
    }

    // --- Time Range Check ---
    // Only check time if a time range is actually specified
    if (startTimeStr || endTimeStr) {
      if (startTimeStr && appointmentTimeOnlyStr < startTimeStr) {
        // console.log(`Slot ${dateTimeString} is before start time ${startTimeStr}.`);
        return false;
      }
      if (endTimeStr && appointmentTimeOnlyStr > endTimeStr) {
        // console.log(`Slot ${dateTimeString} is after end time ${endTimeStr}.`);
        return false;
      }
    }

    // --- Day of Week Check ---
    if (dayOfWeekFilter && Array.isArray(dayOfWeekFilter) && dayOfWeekFilter.length > 0 && dayOfWeekFilter.length < 7) { // Only filter if not all days are selected
      const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
      const appointmentDayName = days[appointmentFullDate.getDay()];
      if (!dayOfWeekFilter.includes(appointmentDayName)) {
        console.log(`Slot ${dateTimeString} (Day: ${appointmentDayName}) is not in the preferred days: ${dayOfWeekFilter.join(', ')}.`);
        return false;
      }
    }
    // console.log(`Appointment ${dateTimeString} is within range [Date: ${startDateStr || 'N/A'}-${endDateStr || 'N/A'}, Time: ${startTimeStr || 'N/A'}-${endTimeStr || 'N/A'}, DayFilter: ${dayOfWeekFilter ? dayOfWeekFilter.join(',') : 'All'}]`);
    return true;

  } catch (e) {
    console.error('Error comparing dates/times:', e);
    return false;
  }
}

// Function to confirm the appointment in the popup
function confirmAppointment(slotType, appointmentDateTime) {
  const confirmButton = document.querySelector('button[data-tls-value="confirm"]');
  if (confirmButton) {
    // Look for the appointment date/time in the popup body
    const popupBodySlot = document.querySelector('.tls-popup--body-slot');
    if (popupBodySlot && !appointmentDateTime) {
      // Get all text content from the popup slot
      const slotContent = popupBodySlot.textContent.trim();

      // Extract the last 16 characters which should be the date and time (2025-04-15 16:00)
      if (slotContent.length >= 16) {
        appointmentDateTime = slotContent.slice(-16).trim();
        // Determine slot type based on content
        console.log('Slot content:', slotContent);
        // Verify it matches the expected format YYYY-MM-DD HH:MM
        if (!/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}/.test(appointmentDateTime)) {
          appointmentDateTime = '';
        } else {
          console.log(`Found appointment date/time: ${appointmentDateTime}`);
        }
      }
    }

    // If we couldn't extract using the primary method, try with regex as fallback
    if (!appointmentDateTime) {
      const popupBody = document.querySelector('.tls-popup--body');
      if (popupBody) {
        const bodyContent = popupBody.textContent.trim();
        // Determine slot type based on content
        console.log('Body content:', bodyContent);
        const dateTimeMatch = bodyContent.match(/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}/);
        if (dateTimeMatch) {
          appointmentDateTime = dateTimeMatch[0];
          console.log(`Found appointment date/time (fallback): ${appointmentDateTime}`);
        }
      }
    }
    function confirmSlot(confirmButton, slotType, appointmentDateTime) {
      // Then click the button to navigate to the next page
      confirmButton.click();
      // Look for the "Proceed" button on the payment page
      setTimeout(() => {
        console.log('Looking for Proceed button on payment page');
        // Find the proceed button by its class and uppercase text
        const proceedButton = document.querySelector('.tls-checkout--action .tls-button-primary.-uppercase');
        let confirmed_flag = false;
        if (proceedButton) {
          console.log('Found Proceed button, clicking to continue to payment');
          proceedButton.click();
          confirmed_flag = true;
        } else {
          console.log('Proceed button not found on payment page');
          // Try alternative selectors if the primary one fails
          const altProceedButton = document.querySelector('.tls-checkout--action button');
          if (altProceedButton) {
            console.log('Found alternative Proceed button, clicking');
            altProceedButton.click();
            confirmed_flag = true;
          } else {
            console.error('Could not find any Proceed button on the payment page');
            const msg = contentText('slotBookingFailed', {
              datetime: appointmentDateTime
            });
            showWarningBanner(msg);
          }
        }
        if (confirmed_flag) {
          console.log('Appointment confirmed! Waiting for payment page...');
          // Notify background script of successful confirmation
          console.log('Found confirmation button, clicking...');
          // Notify the background script of successful confirmation first
          notifyAppointmentStatus(true, {
            datetime: appointmentDateTime,
            slotType: slotType
          });
          notifyAppointmentFound(slotType);
        }
        storeAppointmentSlot(slotType, appointmentDateTime, true, confirmed_flag, null);
      }, 3000); // Wait 3 seconds for the payment page to fully load
      // Placeholder for payment page handling
      setTimeout(() => {
        console.log('This is where payment processing would happen');
        // paymentPageHandler();
      }, 2000);
    }
    // 1. Legacy free-tier date/time gate. Premium is now enabled for all users, so this should remain inactive.
    if (!entitlementActive && !isWithinDateAndTimeRange(appointmentDateTime, startDate, startTime, endDate, endTime, dayOfWeek)) { // Added dayOfWeek
      console.log('Appointment is outside the specified date/time range or preferred day of week, canceling booking.');
      const cancelButton = document.querySelector('.tls-popup-action .tls-button-danger');
      if (cancelButton) {
        cancelButton.click();
        console.log('Canceled booking due to date/time range.');
        // Notify background script about cancellation/failure
        console.log('slotType:', slotType, 'firstPrimeSlot:', firstPrimeSlot);
        if (slotType === 'standard' && firstPrimeSlot) {
          console.log('Standard slot outside date/time range, trying prime slot instead');
          setTimeout(() => firstPrimeSlot.click(), 1000);
          setTimeout(() => confirmAppointment('prime', appointmentDateTime), 500);
        } else {
          // Reset appointment found status to continue monitoring
          appointmentFound = false;
          confirmAttempts = 0;
          notifyAppointmentStatus(false, { reason: 'outside_date_time_range' });
        }
      } else {
        console.error('Could not find cancel button after date/time range check.');
        // Consider how to handle this - maybe notify background anyway?
        notifyAppointmentStatus(false, { reason: 'cancel_button_not_found_after_date_time_range_check' });
      }
      return;
    }

    // 2. Check prime slot preference if applicable
    if (slotType === 'prime') {
      if (allowPrimeSlots === false) {
        console.log('Prime slot found but not allowed by user settings, canceling');
        // Find and click the cancel button
        const cancelButton = document.querySelector('.tls-popup-action .tls-button-danger');
        if (cancelButton) {
          cancelButton.click();
          console.log('Canceled prime slot booking');
          // Reset appointment found status to continue monitoring
          appointmentFound = false;
          confirmAttempts = 0;
          // Notify background script about cancellation/failure
          notifyAppointmentStatus(false, { reason: 'prime_slot_not_allowed' });
        } else {
          console.error('Could not find cancel button for prime slot cancellation.');
          notifyAppointmentStatus(false, { reason: 'cancel_button_not_found_for_prime' });
        }
        return;
      } else {
        console.log('Prime slot allowed by user settings, proceeding with booking');
        confirmSlot(confirmButton, slotType, appointmentDateTime);
      }
    } else {
      // For non-prime slots (which already passed range check), proceed immediately
      console.log('Standard slot within range, proceeding with booking');
      confirmSlot(confirmButton, slotType, appointmentDateTime);
    }

    // Keep a local debug record if we have a valid datetime.
    if (appointmentDateTime) {
      storeAppointmentSlot(slotType, appointmentDateTime, null, null, null);
    } else {
      console.log('No appointment datetime found, skipping slot storage');
    }
  } else {
    console.log('Confirmation button not found, waiting...');

    // Try a few times then give up
    if (confirmAttempts < 5) {
      confirmAttempts++;
      setTimeout(() => confirmAppointment(slotType, appointmentDateTime), 500);
    } else {
      console.log('Could not find confirmation button after multiple attempts');
      // Notify background script that appointment confirmation failed
      notifyAppointmentStatus(false, {
        reason: 'confirmation_button_not_found'
      });
      // Reset appointment found status to continue monitoring
      appointmentFound = false;
      confirmAttempts = 0; // Reset for next attempt
    }
  }
}

// Function to start monitoring
function startMonitoring() {
  console.log('start monitoring...');

  // Only start if not already monitoring
  if (!isMonitoring) {
    // Check if we're on a valid appointment page
    if (!isValidAppointmentPage()) {
      console.log('Not on a valid TLSContact appointment page, refusing to start monitoring');
      // Notify background that we're not on a valid page
      return;
    }

    isMonitoring = true;
    console.log('Started monitoring for appointment slots');

    // Check for appointment slots immediately
    console.log('Scheduling initial find And Click Appointment call');
    setTimeout(findAndClickAppointmentRouting, 1000);

    // The page load listener was removed as it's redundant.
    // Background script handles triggering checks after page load/refresh.
  } else {
    console.log('Already monitoring, ignoring start monitoring call');
  }
}

/**
 * Finds and clicks the Select button for a specific travel group
 * @param {string|number} groupNumber - The group number to select (e.g., "4006319")
 * @returns {boolean} - Returns true if button was found and clicked, false otherwise
 */
function selectTravelGroup(groupNumber) {
  // Convert to string to ensure consistent comparison
  const targetGroupId = String(groupNumber);

  // Find all submit buttons with name="formGroupId"
  const buttons = document.querySelectorAll('button[type="submit"][name="formGroupId"]');

  // Search for the button with matching value
  for (const button of buttons) {
    if (button.value === targetGroupId) {
      // Found the matching button, click it naturally
      // This preserves referer and other navigation context
      button.click();
      console.log(`Successfully clicked Select button for group ${targetGroupId}`);
      return true;
    }
  }

  // No matching button found, fall back to first button
  if (buttons.length > 0) {
    console.warn(`Select button for group ${targetGroupId} not found. Clicking first available button (group ${buttons[0].value}).`);
    buttons[0].click();
    return true;
  }

  // No buttons found at all
  console.warn(`No travel group buttons found on page.`);
  return false;
}


function navigateToAppointmentBooking(url) {
  // Create a hidden link element
  const link = document.createElement('a');
  link.href = url;
  link.style.display = 'none';
  document.body.appendChild(link);

  // Click it to preserve referer
  link.click();

  // Clean up
  setTimeout(() => document.body.removeChild(link), 100);
}

// Listen for messages from the background script
addRuntimeMessageListener((message, sender, sendResponse) => {
  void handleRuntimeMessage(message, sender, sendResponse);
  return true;
});

async function handleRuntimeMessage(message, sender, sendResponse) {
  console.log('Content script received message:', message.action);
  await getSettings();
  if (message.action === 'getMonitoringStatus') {
    // Report the current monitoring status to the background script
    sendResponse({
      success: true,
      isMonitoring: isMonitoring,
      appointmentFound: appointmentFound
    });
  } else if (message.action === 'resetAppointmentFound') {
    // Reset the appointment found flag to resume monitoring
    appointmentFound = false;
    confirmAttempts = 0;
    sendResponse({ success: true });
  } else if (message.action === 'startContentMonitoring') {
    // Start monitoring from background script request
    console.log('Received startContentMonitoring message');
    if (!isMonitoring) {
      startMonitoring();
      sendResponse({
        success: true,
        started: isMonitoring,
        isValidPage: isValidAppointmentPage()
      });
      // wait for 10 seconds
      await new Promise(resolve => setTimeout(resolve, 10000));
      isMonitoring = false; // TODO:this flag should be removed.
    } else {
      console.log('Already monitoring, not starting again');
      sendResponse({
        success: true,
        started: true,
        alreadyRunning: true
      });
    }
  } else if (message.action === 'clickAppointmentBookingButton') {
    // Detect stuck page that shows "Please try to login with your account"
    if (document.body?.innerText?.includes('Please try to login with your account')) {
      console.warn('Detected stuck page ("Please try to login with your account"). Looking for "Back to Application" link...');
      const backToAppLink = document.querySelector('a#backToApplication');
      if (backToAppLink) {
        console.log('Found "Back to Application" link, clicking it:', backToAppLink.href);
        backToAppLink.click();
      } else {
        console.warn('"Back to Application" link not found, falling back to page reload.');
        window.location.reload();
      }
      return;
    }
    // Detect error page that shows "It looks like something went wrong"
    if (document.body?.innerText?.includes('It looks like something went wrong')) {
      console.warn('Detected error page ("It looks like something went wrong"). Redirecting to TLSContact homepage...');
      window.location.href = 'https://www.tlscontact.com/en/';
      return;
    }
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.has('month')) {
      const apptButton = retryQuerySelector('div[data-testid="appointment-booking"]').querySelector('a');
      apptButton.click();
    } else {
      window.location.reload();
    }
  } else if (message.action === 'RemoveInterceptor') {
    const interceptor = document.querySelector('script[metadata="tlscontactchromeextension"]');
    if (interceptor) {
      interceptor.remove();
      console.log('interceptor removed successfully');
    } else {
      console.log('interceptor not found');
    }
    sendCommandToPage('disable');
  } else if (message.action === 'displayPageWarning') {
    // Display the warning banner requested by the background script
    console.log('Received displayPageWarning message:', message.message);
    showWarningBanner(message.message);
    sendResponse({ success: true }); // Acknowledge receipt
  } else if (message.action === 'autoLogin') {
    console.log('Received autoLogin message from background.');
    handleAutoLogin(message.email, message.password, message.redirectToUrl)
      .then(() => sendResponse({ success: true }))
      .catch(err => {
        console.error("Error during handleAutoLogin:", err);
        sendResponse({ success: false, error: err.message });
      });
    return true; // Indicates async response
  } else if (message.action === 'clickLoginButton') {
    console.log('Received clickLoginButton message from background.');
    // search and click log button 
    //<a href="/en-us/login" class="rounded-full outline-offset-4 outline-primary-200"><span type="button" class="TlsButton_tls-button__syUS5 TlsButton_--outline__Gz93Y TlsButton_primary__sPypD TlsButton_--base__NfSqN !text-sm">LOGIN</span></a>
    const loginButton = document.querySelector('a[href="/en-us/login"]');
    if (loginButton) {
      console.log("onUpdated: Found login button, clicking it.");
      loginButton.click();
    } else {
      console.error("onUpdated: Login button not found on TLSContact main page.");
      // already login in, go to appointment page
      const appointmentPageUrl = message.appointmentPageUrl;
      console.log("onUpdated: Already logged in. Redirecting to appointment page:", appointmentPageUrl);
      // navigateToAppointmentBooking(appointmentPageUrl);
      const appointmentInfo = await extractAppointmentInfo();
      const travelGroupsUrl = "https://" + appointmentInfo.client_code + ".tlscontact.com/en-us/travel-groups";
      console.log("onUpdated: Redirecting to Travel Groups page:", travelGroupsUrl);
      window.location.href = travelGroupsUrl;
    }
    sendResponse({ success: true }); // Acknowledge receipt
  } else if (message.action === 'clickOrderSummaryButton') {
    setTimeout(() => { console.log("Pause for 3 seconds before clickOrderSummaryButton"); }, 3000);
    const button2Selectors = [
      'button[data-testid="btn-basket-confirm"]',
      'button[id="btn-confirm-order-summary"]',
      // button that with data-testid attribute contain confirm
      'button[data-testid*="confirm"]',
      // button that with id attribute contain confirm
      'button[id*="confirm"]',
    ];
    // const confirmButton2 = retryQuerySelector(button2Selector);
    let confirmButton2 = null;
    for (const selector of button2Selectors) {
      try {
        confirmButton2 = document.querySelector(selector);
        if (confirmButton2) {
          break;
        }
      } catch (e) {
        console.warn(`Button2 selector failed: ${selector}`, e);
      }
    }
    if (confirmButton2) {
      // delay for 4 seconds
      console.log('Clicking clickOrderSummaryButton...');
      console.log(confirmButton2)
      confirmButton2.click();
      // storeAppointmentSlot(appointmentTypeDE, appointmentDateTimeDE, true, true, null);
      storeAppointmentSlot('Unknow', '2000-01-01 00:00', true, true, null);
      console.log('Turn off extension');
      notifyAppointmentStatus(true);
      notifyAppointmentFound('unknow'); // Set flag to stop monitoring loop
      appointmentFound = true; // Set flag to stop monitoring loop
    } else {
      console.error('Second confirm button (data-testid="btn-basket-confirm") not found after first click.');
      // Notify failure? Or maybe the process completed? Depends on workflow.
      notifyAppointmentStatus(false, { reason: 'second_confirm_button_not_found' });
      appointmentFound = false; // Allow monitoring to continue if needed
    }
  } else if (message.action === 'clickPaymentButton') {
    // await new Promise(resolve => setTimeout(resolve, 3000));
    setTimeout(() => { console.log("Pause for 3 seconds before clickPaymentButton"); }, 3000);
    let button3Selector = '';
    if (window.location.href.includes('visas-de') || window.location.href.includes('web.visas-fr') || window.location.href.includes('visas-it')) {
      button3Selector = 'button[id^="go-to-tls-payment"]';
    } else {
      button3Selector = 'button[data-testid="btn-go-to-payment"]';
    }
    const button3Selectors = [
      'button[data-testid="btn-go-to-payment"]',
      'button[id^="btn-go-to-payment"]',
      // button that with data-testid attribute contain confirm
      'button[data-testid*="payment"]',
      // button that with id attribute contain confirm
      'button[id*="payment"]',
    ];
    let confirmButton3 = null;
    for (const selector of button3Selectors) {
      try {
        confirmButton3 = document.querySelector(selector);
        if (confirmButton3) {
          break;
        }
      } catch (e) {
        console.warn(`Button3 selector failed: ${selector}`, e);
      }
    }
    if (confirmButton3) {
      console.log('Clicking clickPaymentButton (Proceed to checkout)...');
      confirmButton3.click();
      storeAppointmentSlot(appointmentTypeDE, appointmentDateTimeDE, true, true, true);
      storeAppointmentSlot('Unknow', '2000-01-01 00:00', true, true, true);
      // confirm_btn3_flag = true;
      setTimeout(() => { console.log("Proceed to payment"); }, 3000);
      const radioButton = retryQuerySelector('input[name="payment-type"]');
      radioButton.checked = true;
      const radioButtonTC = retryQuerySelector('#online-payment-understood-globaliris_production');
      radioButtonTC.checked = true;
      const confirmButton4 = retryQuerySelector('#confirm_submit');
      confirmButton4.click();
      console.log('Appointment booking process fully initiated!');
      notifyAppointmentStatus(true);
      notifyAppointmentFound('unknow'); // Set flag to stop monitoring loop
      appointmentFound = true; // Set flag to stop monitoring loop
      return true;
    } else {
      console.error('Third confirm button (data-testid="btn-go-to-payment") not found.');
      // Notify failure? Or maybe the process completed differently?
      // Consider if this is a critical failure or an alternative path.
      notifyAppointmentStatus(false, { reason: 'third_confirm_button_not_found' });
      appointmentFound = false; // Re-enable monitoring if this is a failure
    }
  } else if (message.action === 'goToGlobalPay') {
    notifyAppointmentStatus(true);
    notifyAppointmentFound('unknow'); // Set flag to stop monitoring loop
    appointmentFound = true; // Set flag to stop monitoring loop
    setTimeout(() => { console.log("Proceed to payment"); }, 3000);
    const radioButton = retryQuerySelector('input[name="payment-type"]');
    radioButton.checked = true;
    const radioButtonTC = retryQuerySelector('#online-payment-understood-globaliris_production');
    radioButtonTC.checked = true;
    const confirmButton4 = retryQuerySelector('#confirm_submit');
    confirmButton4.disabled = false;
    confirmButton4.click();
    console.log('Appointment booking process fully initiated!');
    return true;
  } else if (message.action === 'clickContinueButton') {
    setTimeout(() => { console.log("Proceed to appointment booking"); }, 3000);
    const continueButton = retryQuerySelector('#book-appointment-btn');
    continueButton.click();
    return true;
  } else if (message.action === 'clickSelectButton') {
    setTimeout(() => { console.log("Proceed to appointment booking"); }, 3000);
    const appointmentInfo = await extractAppointmentInfo();
    selectTravelGroup(appointmentInfo.appointment_id);
    return true;
  }
  return true; // Indicates async response
}

// Function to extract appointment ID and center name from URL
async function extractAppointmentInfo() {
  const storedParamsResult = await storageGet("urlParamsForRules");
  const storedParams = storedParamsResult["urlParamsForRules"];
  return {
    center_name: storedParams?.centerName,
    appointment_id: storedParams?.formGroupId,
    client_code: storedParams?.clientCode
  };
}

// Slot storage was previously tied to remote login. Keep this as local debug output.
function storeAppointmentSlot(slotType, appointmentDateTime, confirm_btn1, confirm_btn2, confirm_btn3) {
  console.debug('Appointment slot observed:', {
    slotType,
    appointmentDateTime,
    confirm_btn1,
    confirm_btn2,
    confirm_btn3
  });
}

// Modify the notifyAppointmentFound function to remove the slot storage
function notifyAppointmentFound(slotType) {
  console.log(`notifyAppointmentFound: ${slotType}`);
  appointmentFound = true;
  runtimeSendMessage({
    action: 'appointmentFound',
    found: true,
    slotType: slotType
  });
}

// Notify background script when appointment is confirmed or failed
function notifyAppointmentStatus(confirmed, details = {}) {
  const message = {
    action: 'appointmentStatus',
    confirmed: confirmed
  };

  // Add slot type if it was previously stored
  if (details.slotType) {
    message.slotType = details.slotType;
  }

  // Add additional details
  if (confirmed) {
    if (details.datetime) message.datetime = details.datetime;
  } else {
    message.reason = details.reason || 'unknown_error';
  }
  runtimeSendMessage(message).then((response) => {
    if (response?.success) {
      console.log('Appointment status acknowledged by background script.');
    }
  });
}


// --- In-Page Warning Banner ---
const WARNING_BANNER_ID = 'tls-rate-limit-warning-banner';

function showWarningBanner(messageText) {
  const existingBanner = document.getElementById(WARNING_BANNER_ID);
  if (existingBanner) {
    existingBanner.remove();
  }

  const banner = document.createElement('div');
  banner.id = WARNING_BANNER_ID;
  banner.setAttribute('role', 'alert');
  Object.assign(banner.style, {
    position: 'fixed',
    top: '16px',
    left: '50%',
    transform: 'translateX(-50%)',
    display: 'grid',
    gridTemplateColumns: '34px minmax(0, 1fr) 28px',
    alignItems: 'center',
    gap: '12px',
    width: 'min(760px, calc(100vw - 28px))',
    padding: '12px 14px',
    border: '1px solid rgba(255, 255, 255, 0.55)',
    borderRadius: '18px',
    background: 'linear-gradient(135deg, rgba(33, 33, 36, 0.88), rgba(61, 63, 70, 0.72))',
    color: '#f5f7fb',
    boxShadow: '0 18px 42px rgba(0, 0, 0, 0.28), inset 0 1px rgba(255, 255, 255, 0.35)',
    backdropFilter: 'blur(22px) saturate(1.45)',
    WebkitBackdropFilter: 'blur(22px) saturate(1.45)',
    zIndex: '2147483647',
    boxSizing: 'border-box',
    fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif'
  });

  const icon = document.createElement('span');
  icon.textContent = '!';
  Object.assign(icon.style, {
    display: 'grid',
    placeItems: 'center',
    width: '34px',
    height: '34px',
    borderRadius: '50%',
    background: 'linear-gradient(135deg, rgba(255, 196, 87, 0.95), rgba(255, 119, 75, 0.9))',
    color: '#1d1308',
    fontSize: '20px',
    fontWeight: '900',
    boxShadow: 'inset 0 1px rgba(255,255,255,0.7), 0 6px 14px rgba(255, 143, 56, 0.25)'
  });

  const copy = document.createElement('span');
  Object.assign(copy.style, {
    display: 'grid',
    gap: '2px',
    minWidth: '0'
  });

  const title = document.createElement('strong');
  title.textContent = contentText('bannerTitle');
  Object.assign(title.style, {
    color: '#ffffff',
    fontSize: '13px',
    fontWeight: '780',
    letterSpacing: '0',
    lineHeight: '1.2'
  });

  const messageSpan = document.createElement('span');
  messageSpan.textContent = messageText;
  Object.assign(messageSpan.style, {
    color: 'rgba(245, 247, 251, 0.82)',
    fontSize: '12px',
    fontWeight: '560',
    lineHeight: '1.35'
  });
  copy.append(title, messageSpan);

  const closeBtn = document.createElement('button');
  closeBtn.type = 'button';
  closeBtn.textContent = 'x';
  closeBtn.setAttribute('aria-label', contentText('dismiss'));
  closeBtn.title = contentText('dismiss');
  Object.assign(closeBtn.style, {
    width: '28px',
    height: '28px',
    border: '0',
    borderRadius: '50%',
    background: 'rgba(255, 255, 255, 0.14)',
    color: 'rgba(255, 255, 255, 0.9)',
    cursor: 'pointer',
    fontSize: '16px',
    fontWeight: '760',
    lineHeight: '1'
  });
  closeBtn.onclick = () => banner.remove();
  banner.append(icon, copy, closeBtn);

  if (document.body) {
    document.body.prepend(banner);
    console.log('Displayed rate limit warning banner on page.');
  } else {
    window.addEventListener('DOMContentLoaded', () => document.body.prepend(banner));
    console.log('DOM not ready, scheduled banner display.');
  }
}


/**
 * Scans the available time slots in the HTML structure, including pagination.
 *
 * @returns {Promise<Object>} A Promise that resolves with an object where keys are
 *                            date strings (YYYY-MM-DD) and values are objects.
 *                            The nested objects have time strings (HH:mm) as keys
 *                            and objects containing 'standard' and 'prime' buttons
 *                            (or null if not available) as values.
 *                            Example:
 *                            {
 *                                "2025-05-25": {
 *                                    "07:00": { standard: ButtonElement, prime: ButtonElement },
 *                                    "07:30": { standard: ButtonElement, prime: null }
 *                                },
 *                                "2025-05-26": { ... }
 *                            }
 */
async function scrapeTimeSlots() {
  const allTimeSlots = {};
  const monthMap = {
    "JAN": 0, "FEB": 1, "MAR": 2, "APR": 3, "MAY": 4, "JUN": 5,
    "JUL": 6, "AUG": 7, "SEP": 8, "OCT": 9, "NOV": 10, "DEC": 11,
    "FÉV": 2, "DÉC": 11, "AVR": 3, "MAI": 4, "JUI": 6, "AOÛ": 7
  };

  while (true) {
    // Find all date slots on the current page view
    const slots = document.querySelectorAll('.tls-time-group--slot');

    if (slots.length === 0) {
      // No slots found on the current view, stop pagination
      break;
    }

    const today = new Date();
    today.setHours(0, 0, 0, 0); // Set to start of day for comparison

    // Process slots on the current page
    for (const slot of slots) {
      const headerTitleElement = slot.querySelector('.tls-time-group--header-title');
      if (!headerTitleElement) {
        console.warn('Could not find header title for a slot.');
        continue; // Skip this slot if header is missing
      }

      const headerText = headerTitleElement.textContent.trim();
      const parts = headerText.split('. '); // e.g., "MAY. 25"
      if (parts.length !== 2) {
        console.warn(`Unexpected header format: ${headerText}`);
        // continue; // Skip if format is unexpected
      }

      const monthAbbr = parts[0].toUpperCase();
      const day = parseInt(parts[1], 10);

      if (!(monthAbbr in monthMap) || isNaN(day)) {
        console.warn(`Could not parse month or day from header: ${headerText}`);
        continue; // Skip if parsing fails
      }

      const month = monthMap[monthAbbr];

      // Infer the year: Use current year unless the date is in the past,
      // in which case it must be the next year.
      let year = today.getFullYear();
      let potentialDate = new Date(year, month, day, 3);

      // Check if the potential date is before today (month/day comparison matters)
      // Create a date object for comparison ignoring time
      const potentialDateComparison = new Date(year, month, day);
      potentialDateComparison.setHours(0, 0, 0, 0);

      // If the calculated date is before today, assume it's next year
      if (potentialDateComparison < today) {
        year++;
        potentialDate = new Date(year, month, day);
      }

      // Format date as YYYY-MM-DD
      const formattedDate = potentialDate.toISOString().split('T')[0];

      // Initialize the date entry if it doesn't exist
      if (!allTimeSlots[formattedDate]) {
        allTimeSlots[formattedDate] = {};
      }

      // Find all time items within this specific slot
      const timeItems = slot.querySelectorAll('.tls-time-group--item');

      for (const item of timeItems) {
        // Find the available button within the item
        const button = item.querySelector('button.tls-time-unit.-available');

        if (button) {
          const timeText = button.textContent.trim();

          // Determine if it's standard or prime
          const type = button.classList.contains('-prime') ? 'prime' : 'standard';

          // Initialize the time entry if it doesn't exist for this date
          if (!allTimeSlots[formattedDate][timeText]) {
            allTimeSlots[formattedDate][timeText] = { standard: null, prime: null };
          }

          // Store the button element
          allTimeSlots[formattedDate][timeText][type] = button;
        }
      }
    }

    // --- Pagination Logic ---
    // Find the time picker container first
    const timePickerContainer = document.querySelector('div.tls-time-picker');
    let nextPageButton = null;

    if (timePickerContainer) {
      // Find the next page button specifically within the time picker
      nextPageButton = timePickerContainer.querySelector('button.tls-time-picker--arrow svg[data-icon="chevron-right"]');
      if (nextPageButton) {
        // Get the actual button element from the found SVG
        nextPageButton = nextPageButton.closest('button');
      }
    }

    // Check if the next page button exists and is not disabled
    // A button is considered disabled if it has the 'disabled' attribute
    // or potentially a specific class like '-disabled'. Check the HTML.
    // Based on provided structure, checking the 'disabled' attribute is standard.
    // You might also need to check for a class like '-disabled' if the attribute isn't used.
    const isButtonDisabled = nextPageButton ? nextPageButton.disabled || nextPageButton.classList.contains('-disabled') : true;


    if (nextPageButton && !isButtonDisabled) {
      // Click the button and wait for the next set of slots to load
      try {
        nextPageButton.click();
        // Wait for a brief moment for the page to update.
        // The exact time needed might vary depending on the page's loading mechanism.
        // A more robust solution would involve waiting for a specific element
        // to appear or disappear.
        await new Promise(resolve => setTimeout(resolve, 500)); // Wait 500ms
      } catch (error) {
        console.error("Error clicking next page button:", error);
        // If clicking fails, stop the process
        break;
      }
    } else {
      // No more next page button or it's disabled, stop pagination
      break;
    }
  }

  return allTimeSlots;
}

// Example usage (you would call this function when the time picker is visible)
/*
async function runScrape() {
    console.log("Starting time slot scraping...");
    const availableSlots = await scrapeTimeSlots();
    console.log("Finished scraping. Available slots:");
    console.log(availableSlots);

    // Example of how to access a specific button element
    // const may25_7am_standard_button = availableSlots['2025-05-28']?.['17:00']?.['prime'];
    // if (may25_7am_standard_button) {
    //     console.log("Found 2025-05-25 07:00 Standard button:", may25_7am_standard_button);
    //     // You could then click this button: may25_7am_standard_button.click();
    // } else {
    //     console.log("2025-05-25 07:00 Standard slot not found or not available.");
    // }
}

// Call the function when the page is ready or when the time picker appears
// runScrape();
*/

/**
 * Filters appointment data based on global time filter variables
 * @param {Object} data - The appointment data object with date keys and time slot values
 * @returns {Object} - Filtered appointment data, empty object {} if no slots match
 */
function filterAppointmentsByTimeRange(data) {
  // Return empty object if input data is null, undefined, or empty
  if (!data || Object.keys(data).length === 0) {
    return {};
  }

  const filteredData = {};

  // Loop through each date in the data
  for (const dateKey in data) {
    if (data.hasOwnProperty(dateKey)) {
      const timeSlots = data[dateKey];
      const filteredTimeSlots = {};

      // Loop through each time slot for this date
      for (const timeKey in timeSlots) {
        if (timeSlots.hasOwnProperty(timeKey)) {
          // Combine date and time into a single datetime string for filtering
          const dateTimeString = `${dateKey} ${timeKey}`;

          // Check if this slot passes the time filters
          if (isWithinDateAndTimeRange(
            dateTimeString,
            startDate,
            startTime,
            endDate,
            endTime,
            dayOfWeek
          ) && data[dateKey][timeKey]) {
            // Keep this time slot
            filteredTimeSlots[timeKey] = timeSlots[timeKey];
          }
        }
      }

      // Only add the date to filtered data if it has at least one valid time slot
      if (Object.keys(filteredTimeSlots).length > 0) {
        filteredData[dateKey] = filteredTimeSlots;
      }
    }
  }

  console.log(`Original slots: ${getTotalSlotCount(data)}, Filtered slots: ${getTotalSlotCount(filteredData)}`);
  return filteredData;
}

/**
 * Helper function to count total number of time slots in appointment data
 * @param {Object} data - The appointment data object
 * @returns {number} - Total number of time slots
 */
function getTotalSlotCount(data) {
  let count = 0;
  for (const dateKey in data) {
    if (data.hasOwnProperty(dateKey)) {
      count += Object.keys(data[dateKey]).length;
    }
  }
  return count;
}

async function checkAppointment(centerName, groupId, appointmentType) {
  appointmentType = appointmentType === 'standard' ? 'normal' : 'prime%20time';
  try {
    let origin = window.location.origin;
    // visas-be.tlscontact.com
    // client is origin split by dot, take the first part and remove visas-
    let client = window.location.host.split('.')[0].replace('visas-', '');
    // client is leg_de if legalization-de else same
    client = client === 'legalization-de' ? 'leg_de' : client;
    let url = `${origin}/services/customerservice/api/tls/appointment/${centerName.substring(0, 2)}/${centerName}/table?client=${client}&formGroupId=${groupId}&appointmentType=${appointmentType}&appointmentStage=appointment`;
    const r = await fetch(url, {
      headers: {
        "accept": "application/json, text/plain, */*",
        "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
        "cache-control": "no-cache",
        "pragma": "no-cache",
        "priority": "u=1, i",
        "sec-ch-ua": "\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\"",
        "sec-ch-ua-arch": "\"arm\"",
        "sec-ch-ua-bitness": "\"64\"",
        "sec-ch-ua-full-version": "\"135.0.7049.115\"",
        "sec-ch-ua-full-version-list": "\"Google Chrome\";v=\"135.0.7049.115\", \"Not-A.Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"135.0.7049.115\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-model": "\"\"",
        "sec-ch-ua-platform": "\"macOS\"",
        "sec-ch-ua-platform-version": "\"14.5.0\"",
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
        "x-xsrf-token": getCookie('XSRF-TOKEN')
      },
      referrer: `${origin}/appointment/${centerName.substring(0, 2)}/${centerName}/${groupId}`,
      referrerPolicy: "strict-origin-when-cross-origin",
      body: null,
      method: "GET",
      mode: "cors",
      credentials: "include"
    });

    const data = await r.json();
    console.log(appointmentType, data);
    return data;
  } catch (error) {
    console.error('Error fetching appointment data:', error);
    return null;
  }
}

function getExtensionStatus() {
  return runtimeSendMessage({ action: 'getStatus' }).then((response) => response || {
    isRunning: false,
    AppointmentInProgress: false,
    appointmentInProgress: false
  });
}

window.getExtensionStatus = getExtensionStatus;


async function bookAppointment(centerName, groupId, appointmentType, availableSlots) {
  const status = await getExtensionStatus();
  const isRunning = status.isRunning;
  const appointmentInProgress = Boolean(status.appointmentInProgress || status.AppointmentInProgress);
  console.log('Extension status:', { isRunning, appointmentInProgress });

  if (!isRunning || appointmentInProgress) {
    console.log('Extension is not running or appointment is already in progress, aborting booking attempt');
    return;
  }
  let data = await checkAppointment(centerName, groupId, appointmentType);
  data = filterAppointmentsByTimeRange(data);
  console.log('Filtered appointment data:', data);
  // Check if data is not null and not empty
  if (data && Object.keys(data).length > 0) {
    // Get the first available date
    const firstDate = Object.keys(data)[0];

    // Get the first available time for that date
    const firstTime = Object.keys(data[firstDate])[0];

    console.log(`First available slot: ${firstDate} at ${firstTime} of type ${appointmentType}. Select from available slots:`, availableSlots);
    // Try to get the button for this slot (try both prime and standard)
    const slotButton = availableSlots[firstDate]?.[firstTime][appointmentType];
    slotButton.click()
    const appointmentDateTime = `${firstDate} ${firstTime}`;
    setTimeout(() => confirmAppointment(appointmentType, appointmentDateTime), 500);
  }
}

/**
 * Filters appointment data based on global time filter variables and converts to generateAppointmentSlots format
 * @param {Object} data - The appointment data object from checkAppointmentFr
 * @returns {Object} - Filtered appointment data in generateAppointmentSlots format, empty object {} if no slots match
 */
function filterAppointmentsByTimeRangeFr(data, appointmentType) {
  // Return empty object if input data is null, undefined, or empty
  if (!data || !data.slots || Object.keys(data.slots).length === 0) {
    return {};
  }

  const filteredData = {};

  // Loop through each date in the data.slots
  for (const dateKey in data.slots) {
    if (data.slots.hasOwnProperty(dateKey)) {
      // console.log(`Processing date: ${dateKey}`);
      const dateSlots = data.slots[dateKey];
      const filteredTimeSlots = {};

      // Loop through each hour entry for this date
      for (const hourEntry of dateSlots) {
        // console.log(`Processing hour entry:`, hourEntry);
        for (const timeKey in hourEntry) {
          // console.log(`Processing time slot: ${timeKey}`);
          if (hourEntry.hasOwnProperty(timeKey)) {
            const slotTypes = hourEntry[timeKey];

            // Skip if the slot types array is empty
            if (!Array.isArray(slotTypes) || slotTypes.length === 0) {
              continue;
            }

            // Combine date and time into a single datetime string for filtering
            const dateTimeString = `${dateKey} ${timeKey}`;

            // Check if this slot passes the time filters
            if (isWithinDateAndTimeRange(
              dateTimeString,
              startDate,
              startTime,
              endDate,
              endTime,
              dayOfWeek
            )) {
              // Convert slot types to the expected format. if '', use standard. otherwise keep as is.
              const validTypes = slotTypes.map(type => {
                if (type === '') {
                  return 'standard'; // Convert empty string to 'standard'
                }
                return type; // Keep other types as is
              }).filter(type => type == appointmentType); // Filter out any empty or whitespace-only types
              if (validTypes.length > 0) {
                // Keep this time slot with valid types
                filteredTimeSlots[timeKey] = validTypes;
              }
            }
          }
        }
      }

      // Only add the date to filtered data if it has at least one valid time slot
      if (Object.keys(filteredTimeSlots).length > 0) {
        filteredData[dateKey] = filteredTimeSlots;
      }
    }
  }

  console.log(`Original slots: ${getTotalSlotCountFr(data)}, Filtered slots: ${getTotalSlotCountFr({ slots: filteredData })}`);
  return filteredData;
}

/**
 * Helper function to count total slots in French appointment data format
 * @param {Object} data - The appointment data object
 * @returns {number} - Total number of slots
 */
function getTotalSlotCountFr(data) {
  let count = 0;
  if (!data || !data.slots) return count;

  for (const dateKey in data.slots) {
    if (data.slots.hasOwnProperty(dateKey)) {
      if (Array.isArray(data.slots[dateKey])) {
        // Format from checkAppointmentFr (array of hour objects)
        for (const hourEntry of data.slots[dateKey]) {
          for (const timeKey in hourEntry) {
            if (hourEntry.hasOwnProperty(timeKey)) {
              const slotTypes = hourEntry[timeKey];
              if (Array.isArray(slotTypes)) {
                count += slotTypes.filter(type => type !== null).length;
              }
            }
          }
        }
      } else {
        // Format from filterAppointmentsByTimeRangeFr (direct time slots)
        for (const timeKey in data.slots[dateKey]) {
          if (data.slots[dateKey].hasOwnProperty(timeKey)) {
            const slotTypes = data.slots[dateKey][timeKey];
            if (Array.isArray(slotTypes)) {
              count += slotTypes.filter(type => type !== null).length;
            }
          }
        }
      }
    }
  }
  return count;
}

/**
 * Books the first available appointment slot that matches the filter criteria
 * @param {string} centerName - The center name for the API call
 * @param {string} groupId - The group ID for the API call
 * @param {Object} availableSlots - The available slots from scrapeTimeSlotsFr function
 * @returns {Promise<boolean>} - Returns true if booking was attempted, false otherwise
 */
async function bookAppointmentFr(centerName, groupId, availableSlots) {
  const status = await getExtensionStatus();
  const isRunning = status.isRunning;
  const appointmentInProgress = Boolean(status.appointmentInProgress || status.AppointmentInProgress);
  console.log('Extension status:', { isRunning, appointmentInProgress });

  if (!isRunning || appointmentInProgress) {
    console.log('Extension is not running or appointment is already in progress, aborting booking attempt');
    return;
  }
  try {
    // Get appointment data from API
    const data = await checkAppointmentFr(centerName, groupId);
    if (!data) {
      console.log('No appointment data received from API');
      return false;
    } else if (data === 401) {
      window.location.href = appointment_page_url;
      console.log('Log in session expired, redirecting to appointment page to trigger auto-login');
    }

    // Filter the data based on time range criteria
    const filteredData = filterAppointmentsByTimeRangeFr(data, 'standard');
    const filteredPtaData = filterAppointmentsByTimeRangeFr(data, 'pta');
    // console.log('Filtered appointment data:', filteredData);

    // Check if filtered data is not null and not empty
    if ((!filteredData || Object.keys(filteredData).length === 0) &&
      (!filteredPtaData || Object.keys(filteredPtaData).length === 0)) {
      console.log('No appointments available within the specified time range');
      return false;
    }
    let firstDate = null;
    let firstTime = null;
    let appointmentType = 'null';
    if (Object.keys(filteredData).length > 0) {
      // If standard appointments are available, use them
      firstDate = Object.keys(filteredData)[0];
      firstTime = Object.keys(filteredData[firstDate])[0];
      appointmentType = 'standard';
    } else if (Object.keys(filteredPtaData).length > 0 && allowPrimeSlots) {
      // If no standard appointments, check for PTA appointments
      firstDate = Object.keys(filteredPtaData)[0];
      firstTime = Object.keys(filteredPtaData[firstDate])[0];
      appointmentType = 'pta';
    }
    if (!firstDate || !firstTime) {
      console.log('No valid appointment dates or times found after filtering');
      return false;
    }

    console.log(`First available slot: ${firstDate} at ${firstTime} of type ${appointmentType}`);
    console.log('Available slots from scraper:', availableSlots);

    // Try to get the button for this slot from the scraped data
    const dateSlots = availableSlots[firstDate];
    if (!dateSlots) {
      console.log(`No scraped slots found for date ${firstDate}`);
      return false;
    }

    const timeSlots = dateSlots[firstTime];
    if (!timeSlots) {
      console.log(`No scraped slots found for time ${firstTime} on ${firstDate}`);
      return false;
    }

    // Get the appropriate button based on appointment type
    const slotButton = timeSlots[appointmentType];
    if (!window.location.href.includes('workflow/appointment-booking')) {
      // redirect to apppointment page
      window.location.href = appointment_page_url;
      console.log('Not on appointment page, redirecting to:', appointment_page_url);
      return false
    }
    console.log(`Clicking slot button for ${firstDate} at ${firstTime} (${appointmentType})`);
    storeAppointmentSlot(appointmentType, `${firstDate} ${firstTime}`, null, null, null);
    slotButton.click();
    let confirm_btn1_flag = false;
    let confirm_btn2_flag = false;
    let confirm_btn3_flag = false;
    const confirmButton1 = await asyncRetryQuerySelector('button[data-testid="btn-basket-confirm"]');
    if (confirmButton1) {
      console.log('Clicking first confirm button...');
      confirmButton1.click();
      confirm_btn1_flag = true;
      // Click second confirm button (often the same selector after modal update/navigation)
      await new Promise(resolve => setTimeout(resolve, 3000));
      let confirmButton2 = await asyncRetryQuerySelector('button[data-testid="btn-basket-confirm"]');
      if (confirmButton2) {
        console.log(confirmButton2)
        if (confirmButton2 && confirmButton2.textContent.trim() === 'Confirm') {
          console.log('Second confirm button found and text matches.');
        } else {
          console.log('confirmButton2 not found or text does not match. Query again...');
          await new Promise(resolve => setTimeout(resolve, 3000));
          confirmButton2 = await asyncRetryQuerySelector('button[data-testid="btn-basket-confirm"]');
          console.log(confirmButton2)
        }
        console.log('Clicking second confirm button...');
        confirmButton2.click();
        confirm_btn2_flag = true;
        storeAppointmentSlot(appointmentType, `${firstDate} ${firstTime}`, confirm_btn1_flag, confirm_btn2_flag, false);  // if waiting for button3, below, this process would be terminated due to going to a new page.
        // Click third confirm button (Proceed to checkout)
        await new Promise(resolve => setTimeout(resolve, 3000));
        const confirmButton3 = await asyncRetryQuerySelector('button[data-testid="btn-go-to-payment"]');
        if (confirmButton3) {
          storeAppointmentSlot(appointmentType, `${firstDate} ${firstTime}`, confirm_btn1_flag, confirm_btn2_flag, true);
          console.log('Clicking third confirm button (Proceed to checkout)...');
          confirmButton3.click();
          confirm_btn3_flag = true;
          await new Promise(resolve => setTimeout(resolve, 3000));
          // Appointment in China, need to proceed to payment page to confirm, despite not needing to pay.
          const radioButton = await asyncRetryQuerySelector('input[name="payment-type"]');
          radioButton.checked = true;
          const radioButtonTC = await asyncRetryQuerySelector('#online-payment-understood-globaliris_production');
          radioButtonTC.checked = true;
          const confirmButton4 = await asyncRetryQuerySelector('#confirm_submit');
          confirmButton4.click();
          console.log('Appointment booking process fully initiated!');
          notifyAppointmentStatus(true);
          notifyAppointmentFound(appointmentType); // Set flag to stop monitoring loop
          appointmentFound = true; // Set flag to stop monitoring loop
          return true;
        } else {
          console.error('Third confirm button (data-testid="btn-go-to-payment") not found.');
          console.log('Clicking second confirm button again...');
          confirmButton2 = await asyncRetryQuerySelector('button[data-testid="btn-basket-confirm"]');
          confirmButton2.click();
          // Notify failure? Or maybe the process completed differently?
          // Consider if this is a critical failure or an alternative path.
          notifyAppointmentStatus(false, { reason: 'third_confirm_button_not_found' });
          appointmentFound = false; // Re-enable monitoring if this is a failure
        }
      } else {
        console.error('Second confirm button (data-testid="btn-basket-confirm") not found after first click.');
        // Notify failure? Or maybe the process completed? Depends on workflow.
        notifyAppointmentStatus(false, { reason: 'second_confirm_button_not_found' });
        appointmentFound = false; // Allow monitoring to continue if needed
      }
    } else {
      console.error('First confirm button (data-testid="btn-basket-confirm") not found after clicking slot.');
      notifyAppointmentStatus(false, { reason: 'first_confirm_button_not_found' });
      appointmentFound = false; // Allow monitoring to continue if needed
    }

  } catch (error) {
    console.error('Error in bookAppointmentFr:', error);
    return false;
  }
}
