// --- Declarative Net Request Rule IDs ---
const RULE_ID_BLOCK_PRIME_TIME_WEEKEND = 1;
const RULE_ID_BLOCK_PREMIUM = 2;
const RULE_ID_BLOCK_PRIME_TIME = 3;

function ignoreTransientMessagingError(error) {
  const message = String(error?.message || error || '');
  return message.includes('Receiving end does not exist') ||
    message.includes('Extension context invalidated') ||
    message.includes('The message port closed');
}

async function sendTabMessage(tabId, message, context = 'tab message') {
  try {
    return await chrome.tabs.sendMessage(tabId, message);
  } catch (error) {
    if (!ignoreTransientMessagingError(error)) {
      console.error(`${context}:`, error?.message || error);
    }
    return null;
  }
}

function parseAppointmentBookingPath(pathname) {
  const pathParts = pathname.split('/').filter(Boolean);
  const workflowIndex = pathParts.indexOf('workflow');
  const appointmentBookingIndex = pathParts.indexOf('appointment-booking');

  if (workflowIndex === -1 || appointmentBookingIndex === -1) {
    return {};
  }

  const numericSegmentRegex = /^\d+$/;
  const formGroupIdBeforeWorkflow = pathParts[workflowIndex - 1];
  const centerNameAfterBooking = pathParts[appointmentBookingIndex + 1];
  const formGroupIdAfterCenter = pathParts[appointmentBookingIndex + 2];

  if (numericSegmentRegex.test(formGroupIdAfterCenter || '')) {
    return {
      centerName: centerNameAfterBooking,
      countryCode: centerNameAfterBooking ? centerNameAfterBooking.slice(0, 2).toLowerCase() : undefined,
      pageFormGroupId: formGroupIdAfterCenter
    };
  }

  if (numericSegmentRegex.test(formGroupIdBeforeWorkflow || '')) {
    return {
      pageFormGroupId: formGroupIdBeforeWorkflow
    };
  }

  return {};
}

// --- Declarative Net Request Rule Setup ---
// params should be an object like { clientCode, countryCode, centerName, formGroupId }
async function setupRequestBlockingRules(params) {
  if (!params || !params.clientCode || !params.countryCode || !params.centerName || !params.formGroupId) {
    console.log('URL parameters not available for DNR rules, ensuring our rules are cleared.');
    const existingRules = await chrome.declarativeNetRequest.getDynamicRules();
    const ruleIdsToRemove = existingRules
      .map(rule => rule.id)
      .filter(id => [RULE_ID_BLOCK_PRIME_TIME_WEEKEND, RULE_ID_BLOCK_PREMIUM, RULE_ID_BLOCK_PRIME_TIME].includes(id));

    if (ruleIdsToRemove.length > 0) {
      try {
        await chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: ruleIdsToRemove });
        console.log('Cleared existing managed DNR rules.');
      } catch (error) {
        console.error('Error clearing DNR rules:', error);
      }
    }
    return;
  }

  // 1. Get the current 'allowPrimeSlots' setting from storage
  const settings = await chrome.storage.local.get(STORAGE_KEYS.ALLOW_PRIME_SLOTS);
  const currentAllowPrimeSlots = settings[STORAGE_KEYS.ALLOW_PRIME_SLOTS] !== undefined ? settings[STORAGE_KEYS.ALLOW_PRIME_SLOTS] : true; // Default to true if not set

  console.log(`Setting up DNR rules. Allow Prime Time Slots: ${currentAllowPrimeSlots}`);

  const rulesToAdd = [];
  // Start with all our managed rule IDs to ensure a clean slate for removal.
  // These are the rules we *might* add, so they are candidates for removal before adding.
  const potentialRuleIdsToRemove = [
    RULE_ID_BLOCK_PRIME_TIME_WEEKEND,
    RULE_ID_BLOCK_PREMIUM,
    RULE_ID_BLOCK_PRIME_TIME
  ];

  // Rule to ALWAYS block "prime time weekend"
  // Note: URL encoding for space is %20.
  // client is params.clientCode if params.cientCode does not contain "-", otherwise it is params.clientCode.split("-")[1]
  const client = params.clientCode.includes('-') ? params.clientCode.split('-')[1] : params.clientCode;
  rulesToAdd.push({
    "id": RULE_ID_BLOCK_PRIME_TIME_WEEKEND,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": `|https://${params.clientCode}.tlscontact.com/services/customerservice/api/tls/appointment/${params.countryCode}/${params.centerName}/table?client=${client}&formGroupId=${params.formGroupId}&appointmentType=prime%20time%20weekend*`,
      "resourceTypes": ["xmlhttprequest"]
    }
  });

  // Rule to ALWAYS block "premium"
  rulesToAdd.push({
    "id": RULE_ID_BLOCK_PREMIUM,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": `|https://${params.clientCode}.tlscontact.com/services/customerservice/api/tls/appointment/${params.countryCode}/${params.centerName}/table?client=${client}&formGroupId=${params.formGroupId}&appointmentType=premium*`,
      "resourceTypes": ["xmlhttprequest"]
    }
  });

  // Conditionally block "prime time"
  if (!currentAllowPrimeSlots) {
    rulesToAdd.push({
      "id": RULE_ID_BLOCK_PRIME_TIME,
      "priority": 1,
      "action": { "type": "block" },
      "condition": {
        "urlFilter": `|https://${params.clientCode}.tlscontact.com/services/customerservice/api/tls/appointment/${params.countryCode}/${params.centerName}/table?client=${client}&formGroupId=${params.formGroupId}&appointmentType=prime%20time*`,
        "resourceTypes": ["xmlhttprequest"]
      }
    });
    console.log('Adding rule to block "prime time" appointments.');
  } else {
    console.log('Not adding rule to block "prime time" appointments as they are allowed.');
  }

  try {
    // Fetch current rules to identify which of our managed rules are actually present.
    const existingRules = await chrome.declarativeNetRequest.getDynamicRules();
    const existingRuleIds = existingRules.map(rule => rule.id);

    // Determine which of our managed rules need to be removed.
    // These are rules that are currently active but shouldn't be based on the new `rulesToAdd` list,
    // or all managed rules if we want to ensure a clean slate.
    // A simpler approach: remove all *our* potential rules, then add back the ones that should be active.
    const ruleIdsToRemove = potentialRuleIdsToRemove.filter(id => existingRuleIds.includes(id));


    await chrome.declarativeNetRequest.updateDynamicRules({
      removeRuleIds: ruleIdsToRemove, // Remove only the managed rules that are currently active
      addRules: rulesToAdd // Add only the currently applicable rules
    });
    console.log('DeclarativeNetRequest rules updated successfully.');
    // const currentRules = await chrome.declarativeNetRequest.getDynamicRules();
    // console.log("Current DNR rules:", currentRules);
  } catch (error) {
    console.error('Error updating declarativeNetRequest rules:', error);
  }
}
async function setupRedirectRule() {
  try {
    // Define a list with each item containing filepath and corresponding urlFilter
    const redirectMappings = [
      {
        filepath: "js/appointment-booking/page-1a8fe1911af829dc.js",
        urlFilter: "*/appointment-booking/page-1a8fe1911af829dc.js"
      },
      {
        filepath: "js/service-level/page-d851b014efcd3dee.js",
        urlFilter: "*/service-level/page-d851b014efcd3dee.js"
      },
      {
        filepath: "js/travel-groups/page-795437e953d97a92.js",
        urlFilter: "*/travel-groups/page-795437e953d97a92.js"
      }
    ];

    // Create rules array with unique IDs for each redirect
    const rules = redirectMappings.map((mapping, index) => ({
      id: 100 + index + 1,
      priority: 1,
      action: {
        type: "redirect",
        redirect: {
          url: chrome.runtime.getURL(mapping.filepath)
        }
      },
      condition: {
        urlFilter: mapping.urlFilter,
        resourceTypes: ["script"]
      }
    }));

    // Get IDs of all rules to remove
    const ruleIdsToRemove = rules.map(rule => rule.id);

    await chrome.declarativeNetRequest.updateDynamicRules({
      removeRuleIds: ruleIdsToRemove,
      addRules: rules
    });

    console.log('TLSContact redirect rules set up successfully');
  } catch (error) {
    console.error('Error setting up redirect rules:', error);
  }
}

async function setupCacheRules() {
  const RULE_ID_CACHE_STATIC = 50;
  const RULE_ID_EXCLUDE_BOOKING_OLD = 51;
  const RULE_ID_EXCLUDE_BOOKING = 52;
  const RULE_ID_EXCLUDE_AUTH = 53;
  try {
    const rulesToAdd = [
      // Rule 1: High priority exclusion for appointment-booking (No-op header mod)
      {
        "id": RULE_ID_EXCLUDE_BOOKING,
        "priority": 200,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [{ "header": "X-Extension-No-Cache", "operation": "set", "value": "true" }]
        },
        "condition": {
          "urlFilter": "appointment-booking",
          "resourceTypes": ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "other"]
        }
      },
      {
        "id": RULE_ID_EXCLUDE_BOOKING_OLD,
        "priority": 200,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [{ "header": "X-Extension-No-Cache", "operation": "set", "value": "true" }]
        },
        "condition": {
          "urlFilter": "bookings/available-slots",
          "resourceTypes": ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "other"]
        }
      },
      // Rule 2: High priority exclusion for auth/realms (No-op header mod)
      {
        "id": RULE_ID_EXCLUDE_AUTH,
        "priority": 200,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [{ "header": "X-Extension-No-Cache", "operation": "set", "value": "true" }]
        },
        "condition": {
          "urlFilter": "auth/realms/atlas",
          "resourceTypes": ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "other"]
        }
      },
      // Rule 3: Main caching rule (Lower priority, applies to all other tlscontact URLs)
      {
        "id": RULE_ID_CACHE_STATIC,
        "priority": 100,
        "action": {
          "type": "modifyHeaders",
          "responseHeaders": [
            { "header": "Cache-Control", "operation": "set", "value": "public, max-age=604800, immutable" },
            { "header": "Pragma", "operation": "remove" },
            { "header": "Expires", "operation": "remove" },
            { "header": "ETag", "operation": "remove" },
            { "header": "Last-Modified", "operation": "remove" },
            { "header": "X-Extension-Cached", "operation": "set", "value": "true" }
          ]
        },
        "condition": {
          "urlFilter": "*tlscontact.com*",
          "resourceTypes": ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "other", "ping", "media", "websocket", "webtransport", "webbundle"]
        }
      }
    ];

    await chrome.declarativeNetRequest.updateDynamicRules({
      removeRuleIds: [RULE_ID_CACHE_STATIC, RULE_ID_EXCLUDE_BOOKING_OLD, RULE_ID_EXCLUDE_BOOKING, RULE_ID_EXCLUDE_AUTH],
      addRules: rulesToAdd
    });

    console.log('Static asset cache rules (with exclusions) set up successfully.');
  } catch (error) {
    console.error('Error setting up cache rules:', error);
  }
}

// chrome.declarativeNetRequest.onRuleMatchedDebug.addListener((details) => {
//   console.log('Rule matched:', details);
// });


// --- WebRequest Listener for Status Codes ---
chrome.webRequest.onCompleted.addListener(
  (details) => {
    if ((details.method === "GET" && details.url.includes("api/tls/appointment")) || (details.method === "POST" && details.url.includes("/api/tls/appointment/book"))) {
      try {
        const url = new URL(details.url);
        const appointmentType = url.searchParams.get('appointmentType');

        if (appointmentType === 'Short_Stay' || appointmentType === 'normal' || details.url.includes("/api/tls/appointment/book")) { // Only care about Short_Stay/normal for page warning
          if (details.statusCode === 429 || details.statusCode === 403) { // Check for 429 or 403
            console.warn(`TLSContact API returned ${details.statusCode} for ${appointmentType} appointment check. Stopping monitoring and sending warning to content script.`);

            // Extract retry-after header and convert to hours
            let retryHours = "24";
            if (details.responseHeaders) {
              const retryAfterHeader = details.responseHeaders.find(header =>
                header.name.toLowerCase() === "retry-after");

              if (retryAfterHeader && retryAfterHeader.value) {
                const retrySeconds = parseInt(retryAfterHeader.value, 10);
                if (!isNaN(retrySeconds)) {
                  retryHours = (retrySeconds / 3600).toFixed(2);
                }
              }
            }

            // Send message to content script in the specific tab to display the warning
            if (appointmentTabId) { // Ensure we have a valid tab ID
              runtimeText('shadowBanWarning', { hours: retryHours }).then((warningMessage) => {
                sendTabMessage(appointmentTabId, {
                  action: 'displayPageWarning',
                  message: warningMessage
                }, 'Error sending page warning message to content script');
              });
            } else {
              console.error("Cannot send page warning: appointmentTabId is not set.");
            }

            // Stop monitoring immediately
            // Check if already running to avoid redundant resets if multiple errors come quickly
            if (isRunning) {
              resetState().then(() => {
                console.log(`Monitoring stopped due to API error ${details.statusCode}.`);
              });
            }
          }
        }
      } catch (e) {
        console.error("Error processing request completion for notification:", e, details);
      }
    }
  },
  {
    urls: ["*://fr.tlscontact.com/services/customerservice/api/tls/appointment/*"],
    types: ["xmlhttprequest"]
  },
  ["responseHeaders"] // Add permission to access response headers
);

const ENTITLEMENT_ACTIVE_KEY = 'appointments_entitlementActive';
const UI_LANGUAGE_KEY = 'appointments_uiLanguage';
const OFFSCREEN_DOCUMENT_PATH = 'offscreen.html';

// Define storage keys
const STORAGE_KEYS = {
  IS_RUNNING: 'isRunning',
  APPOINTMENT_TAB_ID: 'appointmentTabId',
  REFRESH_INTERVAL: 'refreshInterval',
  ALLOW_PRIME_SLOTS: 'allowPrimeSlots',
  APPOINTMENT_IN_PROGRESS: 'appointmentInProgress',
  NEXT_REFRESH_TIME: 'nextRefreshTime',
  START_DATE: 'startDate',
  START_TIME: 'startTime',
  END_DATE: 'endDate',
  END_TIME: 'endTime',
  DAY_OF_WEEK: 'dayOfWeek', // Added
  URL_PARAMS_FOR_RULES: 'urlParamsForRules',
  USER_EMAIL: 'userEmail',
  USER_PASSWORD: 'userPassword',
  CURRENT_APPOINTMENT_URL: 'currentAppointmentUrl',
  RINGTONE_ACTIVE: 'ringtoneActive',
  APPOINTMENT_DETAILS: 'appointmentDetails'
};

const RUNTIME_COPY = {
  en: {
    invalidEmail: 'Enter a valid email address for auto login.',
    passwordRequired: 'Enter the password for auto login, or leave both login fields empty.',
    passwordTooShort: 'Password must be at least 8 characters.',
    franceIntervalTooShort: 'France tourist appointment pages require a refresh interval of at least 5 minutes.',
    startPageInstructions: 'Open the TLSContact appointment page first: Services, Book an appointment, then Start Monitoring.',
    invalidAppointmentPage: 'Current tab is not a TLSContact appointment page. Open the correct page and try again.',
    unexpectedError: 'Something went wrong while starting monitoring. Try again from the appointment page.',
    invalidRunningState: 'Monitoring is not in a valid running state.',
    notRunningState: 'Monitoring is not running.',
    licenseRequired: 'Activate a valid OpenTLS license before starting monitoring.',
    licenseExpired: 'Your OpenTLS license has expired.',
    licenseClockRollback: 'System clock rollback detected. Connect to the internet and try again.',
    shadowBanWarning: 'TLSContact is temporarily rejecting booking requests. Visible slots may still fail. Stop manual refreshes, keep OpenTLS paused for about {hours} hours, then resume with OpenTLS handling the refreshes.',
    loginThrottleWarning: 'OpenTLS stopped after repeated login attempts. Wait 30 minutes, confirm the saved email and password, then start monitoring again.'
  },
  zh: {
    invalidEmail: '请输入有效的邮箱地址用于自动登录。',
    passwordRequired: '填写邮箱后也需要填写密码；如果不使用自动登录，请将两项都留空。',
    passwordTooShort: '密码至少需要 8 个字符。',
    franceIntervalTooShort: '法国旅游签证页面的刷新间隔必须至少为 5 分钟。',
    startPageInstructions: '请先打开 TLSContact 预约页面：点击 Services，再点击 Book an appointment，然后回到扩展点击开始监控。',
    invalidAppointmentPage: '当前标签页不是 TLSContact 预约页面。请打开正确页面后重试。',
    unexpectedError: '启动监控时出现异常。请回到预约页面后重试。',
    invalidRunningState: '当前监控状态无效。',
    notRunningState: '监控尚未运行。',
    licenseRequired: '请先激活有效的 OpenTLS 许可证，然后再开始监控。',
    licenseExpired: '你的 OpenTLS 许可证已过期。',
    licenseClockRollback: '检测到系统时间回退。请连接互联网后重试。',
    shadowBanWarning: 'TLSContact 正在暂时拒绝预约请求。即使页面显示有时段，也可能预约失败。请停止手动刷新，暂停 OpenTLS 约 {hours} 小时后再恢复，并让 OpenTLS 自动刷新。',
    loginThrottleWarning: 'OpenTLS 因多次登录尝试而停止。请等待 30 分钟，确认保存的邮箱和密码正确后再重新开始监控。'
  }
};

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

async function getUiLanguage() {
  const result = await chrome.storage.local.get(UI_LANGUAGE_KEY);
  return result[UI_LANGUAGE_KEY] === 'zh' ? 'zh' : 'en';
}

async function runtimeText(key, values = {}) {
  const language = await getUiLanguage();
  const template = RUNTIME_COPY[language]?.[key] || RUNTIME_COPY.en[key] || key;
  return formatRuntimeText(template, values);
}

async function sendRuntimeError(sendResponse, key, values = {}) {
  sendResponse({
    success: false,
    error: await runtimeText(key, values),
    errorKey: key,
    errorParams: values
  });
}

const LICENSE_PUBLIC_KEY_JWK = {
  key_ops: ['verify'],
  ext: true,
  kty: 'EC',
  x: 'iC0v4pG6HT4MfAQOfoTAUKVYqqlXqe9V53efNfcCCOE',
  y: '4ccAxfvQGrXbwnkXj04YoW9yM8rqgAuFE5U4lVz6zQQ',
  crv: 'P-256'
};
let cachedLicensePublicKey = null;
const INSTALL_ID_KEY = 'appointments_installId';
const LICENSE_RECORD_KEY = 'appointments_licenseRecord';
const PREMIUM_UNTIL_KEY = 'appointments_premiumUntil';
const LAST_SEEN_AT_KEY = 'appointments_lastSeenAt';
const TRUSTED_TIME_SOURCES = [
  { type: 'date-header', url: 'https://www.apple.com/' },
  { type: 'date-header', url: 'https://www.baidu.com/' }
];
const CLOCK_ROLLBACK_GRACE_MS = 5 * 60 * 1000;

function base64UrlToBytes(value) {
  const padded = value.replaceAll('-', '+').replaceAll('_', '/').padEnd(
    Math.ceil(value.length / 4) * 4,
    '='
  );
  const binary = atob(padded);
  const bytes = new Uint8Array(binary.length);
  for (let index = 0; index < binary.length; index += 1) {
    bytes[index] = binary.charCodeAt(index);
  }
  return bytes;
}

function parseLicenseRecord(record) {
  const payloadBytes = base64UrlToBytes(record.payload);
  const payloadJson = new TextDecoder().decode(payloadBytes);
  return {
    payload: JSON.parse(payloadJson),
    payloadJson,
    signatureBytes: base64UrlToBytes(record.signature)
  };
}

function parseTime(value) {
  const ms = Date.parse(value);
  return Number.isFinite(ms) ? ms : NaN;
}

async function fetchDateHeaderTime(url) {
  const response = await fetch(url, { cache: 'no-store' });
  const nowMs = parseTime(response.headers.get('date'));
  if (!Number.isFinite(nowMs)) throw new Error('time invalid');
  return nowMs;
}

async function getBackgroundNow() {
  for (const source of TRUSTED_TIME_SOURCES) {
    try {
      if (source.type === 'date-header') {
        return await fetchDateHeaderTime(source.url);
      }
    } catch {
      // Stored-license checks can fall back to the local clock after all UTC sources fail.
    }
  }
  return Date.now();
}

async function verifyStoredLicense() {
  const result = await chrome.storage.local.get([
    INSTALL_ID_KEY,
    LICENSE_RECORD_KEY,
    LAST_SEEN_AT_KEY
  ]);
  const installId = result[INSTALL_ID_KEY];
  const record = result[LICENSE_RECORD_KEY];
  if (!installId || !record?.payload || !record?.signature) {
    await chrome.storage.local.set({
      [ENTITLEMENT_ACTIVE_KEY]: false,
      [PREMIUM_UNTIL_KEY]: ''
    });
    return { valid: false, reason: 'licenseRequired' };
  }

  try {
    const nowMs = await getBackgroundNow();
    const lastSeenAt = Number(result[LAST_SEEN_AT_KEY] || 0);
    if (lastSeenAt && nowMs + CLOCK_ROLLBACK_GRACE_MS < lastSeenAt) {
      throw new Error('licenseClockRollback');
    }
    const parsed = parseLicenseRecord(record);
    if (parsed.payload.product !== 'OpenTLS') throw new Error('licenseRequired');
    if (parsed.payload.installId !== installId) throw new Error('licenseRequired');
    if (!cachedLicensePublicKey) {
      try {
        const keyResponse = await fetch(chrome.runtime.getURL('license-public-key.json'));
        cachedLicensePublicKey = await keyResponse.json();
      } catch {
        cachedLicensePublicKey = LICENSE_PUBLIC_KEY_JWK;
      }
    }
    const publicKey = await crypto.subtle.importKey(
      'jwk',
      cachedLicensePublicKey,
      { name: 'ECDSA', namedCurve: 'P-256' },
      false,
      ['verify']
    );
    const validSignature = await crypto.subtle.verify(
      { name: 'ECDSA', hash: 'SHA-256' },
      publicKey,
      parsed.signatureBytes,
      new TextEncoder().encode(parsed.payloadJson)
    );
    if (!validSignature) throw new Error('licenseRequired');
    const expiresAtMs = parseTime(parsed.payload.expiresAt);
    if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) {
      throw new Error('licenseExpired');
    }
    await chrome.storage.local.set({
      [ENTITLEMENT_ACTIVE_KEY]: true,
      [PREMIUM_UNTIL_KEY]: parsed.payload.expiresAt,
      [LAST_SEEN_AT_KEY]: Math.max(nowMs, lastSeenAt || 0)
    });
    return { valid: true, premiumUntil: parsed.payload.expiresAt };
  } catch (error) {
    const reason = error.message || 'licenseRequired';
    await chrome.storage.local.set({
      [ENTITLEMENT_ACTIVE_KEY]: false,
      [PREMIUM_UNTIL_KEY]: ''
    });
    return { valid: false, reason };
  }
}

// Store reference to the appointment tab
let appointmentTabId = null;
let refreshInterval = null; // Default refresh interval in seconds
let isRunning = false;
let appointmentInProgress = false; // Track if appointment booking is in progress
let nextRefreshTime = null; // Track when the next refresh is scheduled
let allowPrimeSlots = true; // Default to allow prime slots
let startDate = null;     // Added
let startTime = null;     // Added
let endDate = null;       // Added
let endTime = null;       // Added
let dayOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; // Added, default to all days
let ringtoneActive = false;
let appointmentDetails = null;
let creatingOffscreenDocument = null;

let lastLoginTimestamp = null; // Track last login timestamp

// --- State Management Functions ---

// Function to load state from storage
async function loadState() {
  return new Promise((resolve) => {
    chrome.storage.local.get([
      STORAGE_KEYS.IS_RUNNING,
      STORAGE_KEYS.APPOINTMENT_TAB_ID,
      STORAGE_KEYS.REFRESH_INTERVAL,
      STORAGE_KEYS.ALLOW_PRIME_SLOTS,
      STORAGE_KEYS.APPOINTMENT_IN_PROGRESS,
      STORAGE_KEYS.NEXT_REFRESH_TIME,
      STORAGE_KEYS.START_DATE,
      STORAGE_KEYS.START_TIME,
      STORAGE_KEYS.END_DATE,
      STORAGE_KEYS.END_TIME,
      STORAGE_KEYS.DAY_OF_WEEK, // Added
      STORAGE_KEYS.USER_EMAIL,
      STORAGE_KEYS.USER_PASSWORD,
      STORAGE_KEYS.CURRENT_APPOINTMENT_URL,
      STORAGE_KEYS.RINGTONE_ACTIVE,
      STORAGE_KEYS.APPOINTMENT_DETAILS
    ], (result) => {
      // console.log('Loading state from storage:', result);
      isRunning = result[STORAGE_KEYS.IS_RUNNING] || false;
      appointmentTabId = result[STORAGE_KEYS.APPOINTMENT_TAB_ID] || null;
      refreshInterval = result[STORAGE_KEYS.REFRESH_INTERVAL];
      allowPrimeSlots = result[STORAGE_KEYS.ALLOW_PRIME_SLOTS] !== undefined ? result[STORAGE_KEYS.ALLOW_PRIME_SLOTS] : true;
      appointmentInProgress = result[STORAGE_KEYS.APPOINTMENT_IN_PROGRESS] || false;
      nextRefreshTime = result[STORAGE_KEYS.NEXT_REFRESH_TIME] || null;
      startDate = result[STORAGE_KEYS.START_DATE] || null;
      startTime = result[STORAGE_KEYS.START_TIME] || null;
      endDate = result[STORAGE_KEYS.END_DATE] || null;
      endTime = result[STORAGE_KEYS.END_TIME] || null;
      dayOfWeek = Array.isArray(result[STORAGE_KEYS.DAY_OF_WEEK]) && result[STORAGE_KEYS.DAY_OF_WEEK].length > 0 ? result[STORAGE_KEYS.DAY_OF_WEEK] : ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; // Added
      // Load new state variables
      userEmail = result[STORAGE_KEYS.USER_EMAIL] || null;
      userPassword = result[STORAGE_KEYS.USER_PASSWORD] || null;
      currentAppointmentUrl = result[STORAGE_KEYS.CURRENT_APPOINTMENT_URL] || null;
      ringtoneActive = result[STORAGE_KEYS.RINGTONE_ACTIVE] || false;
      appointmentDetails = result[STORAGE_KEYS.APPOINTMENT_DETAILS] || null;

      // console.log('State loaded:', { isRunning, appointmentTabId, refreshInterval, allowPrimeSlots, appointmentInProgress, nextRefreshTime, startDate, startTime, endDate, endTime, dayOfWeek, userEmail, userPassword, currentAppointmentUrl });
      resolve();
    });
  });
}

// Declare new state variables at the global scope
let userEmail = null;
let userPassword = null;
let currentAppointmentUrl = null;
let loginAttempts = 0; // Track login attempts

// Function to save state to storage
async function saveState() {
  if (currentAppointmentUrl && currentAppointmentUrl.includes('workflow/appointment-booking') && !currentAppointmentUrl.includes('en-us')) {
    // making sure this url contains param: &lang=en-us 
    const tempURL = new URL(currentAppointmentUrl);
    tempURL.searchParams.set('lang', 'en-us');
    currentAppointmentUrl = tempURL.toString();
    console.log('Updated currentAppointmentUrl to include lang=en-us:', currentAppointmentUrl);
  }
  const stateToSave = {
    [STORAGE_KEYS.IS_RUNNING]: isRunning,
    [STORAGE_KEYS.APPOINTMENT_TAB_ID]: appointmentTabId,
    [STORAGE_KEYS.REFRESH_INTERVAL]: refreshInterval,
    [STORAGE_KEYS.ALLOW_PRIME_SLOTS]: allowPrimeSlots,
    [STORAGE_KEYS.APPOINTMENT_IN_PROGRESS]: appointmentInProgress,
    [STORAGE_KEYS.NEXT_REFRESH_TIME]: nextRefreshTime,
    [STORAGE_KEYS.START_DATE]: startDate,
    [STORAGE_KEYS.START_TIME]: startTime,
    [STORAGE_KEYS.END_DATE]: endDate,
    [STORAGE_KEYS.END_TIME]: endTime,
    [STORAGE_KEYS.DAY_OF_WEEK]: dayOfWeek, // Added
    [STORAGE_KEYS.CURRENT_APPOINTMENT_URL]: currentAppointmentUrl
  };
  console.log('Saving state to storage:', stateToSave);
  return new Promise((resolve) => {
    chrome.storage.local.set(stateToSave, () => {
      if (chrome.runtime.lastError) {
        console.error('Error saving state:', chrome.runtime.lastError);
      } else {
        console.log('State saved successfully.');
      }
      resolve();
    });
  });
}

// Function to reset state (stop monitoring) and clear storage
async function resetState() {
  console.log('Resetting state and stopping monitoring.');
  isRunning = false;
  appointmentInProgress = false;
  appointmentTabId = null;
  nextRefreshTime = null;

  // Clear alarm
  await chrome.alarms.clear('refreshAppointment');
  console.log('Cleared refresh alarm.');

  // Update icon
  updateIcon(false);

  // Clear relevant storage (keep interval/prime settings)
  const stateToClear = {
    [STORAGE_KEYS.IS_RUNNING]: false,
    [STORAGE_KEYS.APPOINTMENT_TAB_ID]: null,
    [STORAGE_KEYS.APPOINTMENT_IN_PROGRESS]: false,
    [STORAGE_KEYS.NEXT_REFRESH_TIME]: null,
    // [STORAGE_KEYS.URL_PARAMS_FOR_RULES]: null // Clear stored URL params
  };
  console.log('Clearing state in storage:', stateToClear);

  // Also clear the dynamic rules
  await setupRequestBlockingRules(null);

  return new Promise((resolve) => {
    chrome.storage.local.set(stateToClear, () => { // Use set to clear URL_PARAMS_FOR_RULES to null
      if (chrome.runtime.lastError) {
        console.error('Error clearing state:', chrome.runtime.lastError);
      } else {
        console.log('State cleared successfully.');
      }
      resolve();
    });
  });
}

// --- Extension Rule Initialization ---
async function initializeExtensionRules() {
  await setupRedirectRule();
  await setupCacheRules();
}

async function hasOffscreenDocument() {
  if (chrome.offscreen?.hasDocument) {
    return chrome.offscreen.hasDocument();
  }

  if (!chrome.runtime.getContexts) {
    return false;
  }

  const offscreenUrl = chrome.runtime.getURL(OFFSCREEN_DOCUMENT_PATH);
  const contexts = await chrome.runtime.getContexts({
    contextTypes: ['OFFSCREEN_DOCUMENT'],
    documentUrls: [offscreenUrl]
  });
  return contexts.length > 0;
}

async function ensureOffscreenDocument() {
  if (!chrome.offscreen) {
    console.warn('Offscreen API is not available; ringtone cannot play in the background.');
    return false;
  }

  if (await hasOffscreenDocument()) {
    return true;
  }

  if (!creatingOffscreenDocument) {
    creatingOffscreenDocument = chrome.offscreen.createDocument({
      url: OFFSCREEN_DOCUMENT_PATH,
      reasons: ['AUDIO_PLAYBACK'],
      justification: 'Play a ringtone after a TLSContact appointment slot is secured until the user stops it.'
    });
  }

  try {
    await creatingOffscreenDocument;
    return true;
  } catch (error) {
    console.error('Failed to create offscreen ringtone document:', error);
    return false;
  } finally {
    creatingOffscreenDocument = null;
  }
}

async function notifyPopupRingtoneState() {
  chrome.runtime.sendMessage({
    action: 'ringtoneStateChanged',
    ringtoneActive,
    appointmentDetails
  }).catch(() => {
    // Popup is often closed; it will read the persisted state when opened.
  });
}

async function startRingtone(details = {}) {
  const cleanDetails = Object.fromEntries(
    Object.entries(details).filter(([, value]) => value !== null && value !== undefined && value !== '')
  );
  const existingDetails = ringtoneActive && appointmentDetails ? appointmentDetails : {};

  ringtoneActive = true;
  appointmentDetails = {
    ...existingDetails,
    ...cleanDetails,
    securedAt: existingDetails.securedAt || new Date().toISOString()
  };

  await chrome.storage.local.set({
    [STORAGE_KEYS.RINGTONE_ACTIVE]: true,
    [STORAGE_KEYS.APPOINTMENT_DETAILS]: appointmentDetails
  });
  await chrome.action.setBadgeBackgroundColor({ color: '#e11d48' });
  await chrome.action.setBadgeText({ text: '!' });

  if (await ensureOffscreenDocument()) {
    chrome.runtime.sendMessage({ target: 'offscreen', action: 'playRingtone' }).catch((error) => {
      console.error('Failed to start ringtone:', error);
    });
  }

  await notifyPopupRingtoneState();
}

async function stopRingtone() {
  ringtoneActive = false;
  await chrome.storage.local.set({ [STORAGE_KEYS.RINGTONE_ACTIVE]: false });
  await chrome.action.setBadgeText({ text: '' });

  try {
    await chrome.runtime.sendMessage({ target: 'offscreen', action: 'stopRingtone' });
  } catch (error) {
    // The offscreen document may already be closed.
  }

  if (chrome.offscreen && await hasOffscreenDocument()) {
    try {
      await chrome.offscreen.closeDocument();
    } catch (error) {
      console.warn('Could not close offscreen ringtone document:', error);
    }
  }

  await notifyPopupRingtoneState();
}

// Load state when the service worker starts and update icon
(async () => {
  await verifyStoredLicense();
  await loadState();
  const storedParamsResult = await chrome.storage.local.get(STORAGE_KEYS.URL_PARAMS_FOR_RULES);
  const storedParams = storedParamsResult[STORAGE_KEYS.URL_PARAMS_FOR_RULES];
  await initializeExtensionRules(); // Ensure redirect/cache rules are set up on startup
  if (ringtoneActive) {
    await startRingtone(appointmentDetails || {});
  }
  await setupRequestBlockingRules(storedParams); // Call rule setup on SW startup with potentially stored params
  updateIcon(isRunning);

  // If running, ensure the alarm exists (it might have been cleared if SW terminated)
  if (isRunning && appointmentTabId) {
    const alarm = await chrome.alarms.get('refreshAppointment');
    if (!alarm) {
      console.log('Alarm not found while running, recreating alarm.');
      chrome.alarms.create('refreshAppointment', {
        delayInMinutes: 0.1, // Start soon
        periodInMinutes: refreshInterval / 60
      });
      // Recalculate next refresh time based on new alarm schedule
      nextRefreshTime = Date.now() + (refreshInterval * 1000); // Estimate, will be more accurate after first alarm fire
      await saveState();
    } else {
      console.log('Alarm exists:', alarm);
      // Ensure nextRefreshTime is aligned with the actual alarm schedule if possible
      if (alarm.scheduledTime) {
        nextRefreshTime = alarm.scheduledTime;
        await saveState(); // Save potentially corrected time
      }
    }
  } else if (isRunning && !appointmentTabId) {
    // State inconsistency: running but no tab ID. Reset.
    console.warn('Inconsistent state: running=true but no tab ID. Resetting state.');
    await resetState();
  }
})();

// Constants for redirection retry logic
const REDIRECT_WAIT_MS = 4000; // 4 seconds
// let attemptBookingAppointment = false; // Flag to track if booking attempt is in progress

// Add listener for tab updates (navigation, refresh completion)
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, updatedTab) => { // Make listener async

  const updatedUrl = updatedTab?.url || changeInfo.url || '';

  // if (attemptBookingAppointment) {
  //   console.log("onUpdated: Booking attempt in progress. delaying tab updates in case it is unsuccessful.");
  //   await new Promise(resolve => setTimeout(resolve, 60000)); // Wait for 60 seconds
  //   attemptBookingAppointment = false; // Reset the flag after waiting
  // }
  if (appointmentInProgress || !isRunning) {
    // console.log("onUpdated: Appointment in progress or not running. Ignoring tab updates.");
    return
  }
  if (updatedUrl.includes('travel-groups')) {
    sendTabMessage(tabId, { action: 'clickSelectButton' }, 'Error on travel groups page');
    return;
  }
  if (updatedUrl.includes('tlscontact.com/checkout') || updatedUrl.includes('workflow/order-summary') || updatedUrl.includes('workflow/payment') || updatedUrl.includes('workflow/service-level')) {
    if (updatedUrl.includes('workflow/order-summary')) {
      sendTabMessage(tabId, { action: 'clickOrderSummaryButton' }, 'Error on order summary');
    } else if (updatedUrl.includes('workflow/payment')) {
      sendTabMessage(tabId, { action: 'clickPaymentButton' }, 'Error on payment page');
    } else if (updatedUrl.includes('tlscontact') && updatedUrl.includes('payment.')) {
      sendTabMessage(tabId, { action: 'goToGlobalPay' }, 'Error on payment page');
    } else if (updatedUrl.includes('tlscontact') && updatedUrl.includes('service-level')) {
      sendTabMessage(tabId, { action: 'clickContinueButton' }, 'Error on service level page');
    }
    // attemptBookingAppointment = true; // Set flag to indicate booking attempt
    // console.log("onUpdated: Detected checkout page. Setting appointmentInProgress to true.");
    // await new Promise(resolve => setTimeout(resolve, 60000)); // Wait for 60 seconds
    // wait for 30 seconds to carry on, if succesully booked, the appointmentInProgress will be set to True. The function will end below
    console.log("onUpdated: Detected checkout page. Do nothing");
    return
  }

  // --- Handle Monitoring Tab Updates ---
  // Ensure state is loaded before checking appointmentTabId
  // Use a temporary variable to avoid race conditions if state changes during async operations
  const currentMonitoredTabId = appointmentTabId;

  if (isRunning && tabId === currentMonitoredTabId && changeInfo.status === 'complete') {
    console.log(`onUpdated: Monitored tab ${tabId} finished loading. URL: ${updatedUrl}`);

    // Load necessary state variables *after* confirming it's the monitored tab and loading is complete
    await loadState();

    // Double-check if monitoring is still supposed to be running after await
    if (!isRunning || tabId !== appointmentTabId) {
      console.log("onUpdated: Monitoring stopped or tab changed during loadState. Aborting.");
      return;
    }

    const finalUrl = updatedUrl;
    const isOnTargetPage = finalUrl.split('?')[0] === currentAppointmentUrl.split('?')[0];
    const isOnLoginPage = finalUrl && (finalUrl.includes("auth/realms/atlas/protocol/openid-connect") || (finalUrl.includes("tlscontact.com/") && finalUrl.includes("login")));
    const isOnLoginAction = finalUrl && finalUrl.includes("auth/realms/atlas/login-actions");
    const isCloudFlare = finalUrl && finalUrl.includes("challenges.cloudflare.com");

    console.log(`onUpdated: Checking URL. Target: ${currentAppointmentUrl}, Final: ${finalUrl}, IsTarget: ${isOnTargetPage}, IsLogin: ${isOnLoginPage}`);

    if (isOnTargetPage) {
      console.log("onUpdated: Tab is on the correct appointment page. Signaling content script.");
      // Use setTimeout to allow content script to potentially initialize fully
      setTimeout(() => {
        sendTabMessage(tabId, { action: 'startContentMonitoring' }, 'onUpdated: Error sending startContentMonitoring');
      }, 500);
    } else if (isOnLoginPage) {
      console.log("onUpdated: Credentials found. Waiting for 8 seconds before triggering autoLogin.");
      await new Promise(resolve => setTimeout(resolve, 2000 * (loginAttempts + 1))); // Wait for 8 seconds
      const currentTimestamp = Date.now();
      const loginAttemptWindowMs = 60000 * 5;
      if (lastLoginTimestamp && (currentTimestamp - lastLoginTimestamp) < loginAttemptWindowMs && loginAttempts >= 20) {
        console.warn(`Already attempted more than 20 logins recently. Shutting down.`);
        sendTabMessage(tabId, {
          action: 'displayPageWarning',
          message: await runtimeText('loginThrottleWarning')
        }, 'onUpdated: Error sending stopLogin message');
        await resetState();
        return; // Stop further processing
      }
      if (!lastLoginTimestamp || (currentTimestamp - lastLoginTimestamp) > loginAttemptWindowMs) {
        console.log("onUpdated: Last login attempt was more than 5 minutes ago. Resetting login attempts.");
        loginAttempts = 1;
      } else {
        console.log("onUpdated: Last login attempt was within 5 minutes. Incrementing login attempts.");
        loginAttempts += 1; // Increment login attempts
      }
      lastLoginTimestamp = currentTimestamp; // Update last login timestamp
      console.log("onUpdated: Tab landed on the login page.");
      if (userEmail && userPassword && currentAppointmentUrl) {
        sendTabMessage(tabId, {
          action: 'autoLogin',
          email: userEmail,
          password: userPassword,
          redirectToUrl: currentAppointmentUrl // Ensure redirect back to original target
        }, 'onUpdated: Error sending autoLogin message');
      } else if (isOnLoginAction) {
        console.log("onUpdated: Login action detected. Do nothing");
        return
      } else if (isCloudFlare) {
        console.log("onUpdated: isCloudFlare detected. Do nothing");
        return
      } else {
        console.error("onUpdated: Landed on login page but missing credentials or target URL. Stopping monitoring.");
        await resetState();
      }
    } else {
      // Landed on a page that is neither the target nor the login page
      console.warn(`onUpdated: Tab landed on unexpected page: ${finalUrl}. Attempting redirect back to target: ${currentAppointmentUrl}`);
      if (currentAppointmentUrl) {
        if (currentAppointmentUrl.includes('web.visas-fr')) {
          console.warn("onUpdated: Detected new france web page. No redirecting for now.");
          await resetState();
          return;
        }
        // Start the retry mechanism
        if (currentAppointmentUrl.includes('visas-de.') || currentAppointmentUrl.includes('visas-fr.') || currentAppointmentUrl.includes('visas-it.')) {
          console.warn("onUpdated: Detected visade.de URL, redirecting to TLSContact main page.");
          await loadState();
          const storedParamsResult = await chrome.storage.local.get(STORAGE_KEYS.URL_PARAMS_FOR_RULES);
          const params = storedParamsResult[STORAGE_KEYS.URL_PARAMS_FOR_RULES];
          const client = params.clientCode.includes('-') ? params.clientCode.split('-')[1] : params.clientCode;
          // direct to this url https://visas-de.tlscontact.com/en-us/country/gb/vac/gbLON2de
          const tempURL = `https://${params.clientCode}.tlscontact.com/en-us/country/${client}/vac/${params.centerName}`;
          if (finalUrl == tempURL) {
            console.log("onUpdated: Already on DE TLSContact main page, no need to redirect, proceeding to login.");
            return;
          } else {
            console.log("onUpdated: Redirecting to TLSContact main page:", tempURL);
            await chrome.tabs.update(tabId, { url: tempURL });
            // wait for 4 seconds for it to load
            await new Promise(resolve => setTimeout(resolve, REDIRECT_WAIT_MS)); // Wait for 4 seconds
            sendTabMessage(tabId, { action: 'clickLoginButton', appointmentPageUrl: currentAppointmentUrl }, 'Error clicking login button');
            await new Promise(resolve => setTimeout(resolve, REDIRECT_WAIT_MS)); // Wait for 4 seconds  
          }
        } else {
          await chrome.tabs.update(tabId, { url: currentAppointmentUrl });
        }
      } else {
        console.error("onUpdated: Landed on unexpected page but no target URL stored. Stopping monitoring.");
        await resetState();
      }
    }
  } else if (tabId === currentMonitoredTabId && changeInfo.status) {
    // Log other status changes for the monitored tab if needed
    // console.log(`onUpdated: Monitored tab ${tabId} status changed: ${changeInfo.status}`);
  }
});


// Function to update the extension icon based on running state
function updateIcon(running) {
  const iconPath = running ? {
    "16": "images/icon16_running.png",
    "48": "images/icon48_running.png",
    "128": "images/icon128_running.png"
  } : {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  };

  chrome.action.setIcon({ path: iconPath });
}

// Listen for alarm to refresh the page
chrome.alarms.onAlarm.addListener(async (alarm) => {
  if (alarm.name === 'refreshAppointment') {
    // Load the latest state before proceeding
    await loadState();

    console.log('Refresh alarm triggered. Current state:', { isRunning, appointmentTabId, appointmentInProgress });

    // Only refresh if running, have a valid tab ID, and no appointment is in progress
    if (isRunning && appointmentTabId && !appointmentInProgress) {
      console.log('Alarm: Conditions met, proceeding with refresh trigger for tab:', appointmentTabId);
      try {
        // Check if tab still exists and get its current URL before refreshing
        const tab = await chrome.tabs.get(appointmentTabId);
        console.log('Alarm: Current tab URL before refresh:', tab.url);

        // Basic check if URL seems valid before attempting refresh
        if (!tab.url || !tab.url.includes('tlscontact.com')) {
          console.warn(`Alarm: Tab ${appointmentTabId} URL (${tab.url}) doesn't look like a valid page to refresh. Stopping monitoring.`);
          await resetState();
          return;
        }

        // Initiate the refresh by navigating with a cache-busting parameter
        const urlToRefresh = new URL(currentAppointmentUrl);
        // urlToRefresh.searchParams.set('_ts', Date.now());
        if (!currentAppointmentUrl.includes('en-us')) {
          urlToRefresh.searchParams.set('lang', 'en-us'); // Add month parameter to avoid cache issues
        }
        console.log('Alarm: Initiating refresh navigation to:', urlToRefresh.toString());
        if (currentAppointmentUrl.includes('visas-de.') || currentAppointmentUrl.includes('visas-fr.') || currentAppointmentUrl.includes('visas-it.')) {
          // Send click "Appointment booking" button to content:
          console.log('Alarm: Detected DE URL, sending clickAppointmentBookingButton message to content script.');
          sendTabMessage(appointmentTabId, { action: 'clickAppointmentBookingButton' }, 'Error clicking appointment booking button');
        } else {
          await chrome.tabs.update(appointmentTabId, { url: urlToRefresh.toString() });
        }
        // --- IMPORTANT: All logic to handle the *result* of this refresh is now in the onUpdated listener ---

        // Update next refresh time based on the alarm's schedule
        const currentAlarm = await chrome.alarms.get('refreshAppointment');
        if (currentAlarm && currentAlarm.scheduledTime) {
          nextRefreshTime = currentAlarm.scheduledTime;
          console.log('Alarm: Next refresh scheduled time from alarm:', new Date(nextRefreshTime));
        } else {
          // Fallback if alarm info isn't immediately available
          nextRefreshTime = Date.now() + (refreshInterval * 1000);
          console.warn('Alarm: Could not get next scheduled time from alarm, estimating:', new Date(nextRefreshTime));
        }
        await saveState(); // Save the updated nextRefreshTime

      } catch (error) {
        console.error(`Alarm: Error during refresh trigger for tab ${appointmentTabId}:`, error);
        // Assume tab is closed or inaccessible
        await resetState();
      }
    } else {
      console.log('Alarm: Skipping refresh trigger:', { isRunning, appointmentTabId, appointmentInProgress });
      // If state indicates not running or no tab, ensure alarm is cleared
      if (!isRunning || !appointmentTabId) {
        console.log('State indicates not running or no tab ID, ensuring alarm is cleared.');
        await resetState(); // Reset fully to be safe
      } else if (appointmentInProgress) {
        console.log('Appointment in progress, refresh skipped.');
        // Keep alarm, but clear nextRefreshTime visually
        nextRefreshTime = null;
        await saveState();
      }
    }
  }
});


// Listen for messages from popup or content script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  (async () => { // Wrap in async IIFE to allow awaiting saveState/resetState
    // console.log('Received action:', message.action);
    // Handle popup messages
    if (message.action === 'startMonitoring') {
      const licenseStatus = await verifyStoredLicense();
      if (!licenseStatus.valid) {
        await sendRuntimeError(sendResponse, licenseStatus.reason || 'licenseRequired');
        return;
      }
      const newInterval = message.interval;
      const newAllowPrimeSlots = message.allowPrimeSlots !== undefined ? message.allowPrimeSlots : true;
      const newStartDate = message.startDate || null;
      const newStartTime = message.startTime || null;
      const newEndDate = message.endDate || null;
      const newEndTime = message.endTime || null;
      const newDayOfWeek = message.dayOfWeek || ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; // Added

      // --- Helper function for email validation ---
      function isValidEmail(email) {
        // Simple regex for basic email validation
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
      }
      if (message.userEmail) {
        if (!isValidEmail(message.userEmail)) {
          await sendRuntimeError(sendResponse, 'invalidEmail');
          return; // Stop processing here
        } else if (!message.userPassword) {
          await sendRuntimeError(sendResponse, 'passwordRequired');
          return; // Stop processing here
        } else if (message.userPassword.length < 8) {
          await sendRuntimeError(sendResponse, 'passwordTooShort');
          return; // Stop processing here
        }
      }
      refreshInterval = newInterval;
      allowPrimeSlots = newAllowPrimeSlots;
      startDate = newStartDate;
      startTime = newStartTime;
      endDate = newEndDate;
      endTime = newEndTime;
      dayOfWeek = newDayOfWeek; // Added

      console.log('Processing startMonitoring. Interval:', refreshInterval, 'Allow Prime:', allowPrimeSlots, 'Start Date:', startDate, 'Start Time:', startTime, 'End Date:', endDate, 'End Time:', endTime, 'Day of Week:', dayOfWeek);

      try {
        const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
        if (tabs.length > 0 && tabs[0].id && tabs[0].url && (tabs[0].url.includes('tlscontact.com/appointment') || tabs[0].url.includes('workflow/appointment-booking'))) {

          // --- Set Tab ID Immediately ---
          appointmentTabId = tabs[0].id;
          console.log(`startMonitoring: Setting appointmentTabId to ${appointmentTabId}`);

          // --- Get and Clean URL ---
          const tabUrl = tabs[0].url;
          const urlObject = new URL(tabUrl);
          // urlObject.search = ''; 
          // Remove query parameter  _ts from urlObject.search
          // urlObject.searchParams.delete('_ts'); // Remove cache-busting parameter if present
          urlObject.searchParams.delete('month'); // Remove cache-busting parameter if present
          const cleanedUrl = urlObject.toString();
          console.log(`startMonitoring: Original URL: ${tabUrl}, Cleaned URL: ${cleanedUrl}`);

          // --- Parse URL for parameters (using original URL with path) ---
          const originalUrlObject = new URL(tabUrl); // Re-parse original for path parts
          const hostnameParts = originalUrlObject.hostname.split('.'); // e.g., ["fr", "tlscontact", "com"]
          // if hostnameParts[0] is "web" then use hostnameParts[1] as clientCode
          if (hostnameParts[0] === 'web') {
            hostnameParts.shift(); // Remove 'web' part
          }
          const clientCode = hostnameParts[0];
          const pathParts = originalUrlObject.pathname.split('/');
          // Example: /appointment/gb/gbLON2fr/18817949
          let countryCode, centerName, pageFormGroupId;
          if (pathParts.length >= 5 && pathParts[1] === 'appointment') {
            countryCode = pathParts[2];
            centerName = pathParts[3];
            pageFormGroupId = pathParts[4];
          } else if (originalUrlObject.pathname.includes('workflow') && originalUrlObject.pathname.includes('appointment-booking')) {
            // Handle workflow URL structures:
            // Old: https://visas-fr.tlscontact.com/19348308/workflow/appointment-booking
            // New: https://visas-fr.tlscontact.com/workflow/appointment-booking/gbLON2fr/19348308
            const parsedBookingPath = parseAppointmentBookingPath(originalUrlObject.pathname);
            const location = originalUrlObject.searchParams.get('location'); // e.g., "usa"
            pageFormGroupId = parsedBookingPath.pageFormGroupId || null;
            countryCode = parsedBookingPath.countryCode || (location ? location.slice(0, 2).toLowerCase() : 'default');
            centerName = parsedBookingPath.centerName || location;
            console.log("Workflow appointment URL structure detected:", { countryCode, centerName, pageFormGroupId });
          }
          if (!countryCode || !centerName) {
            //get countryCode and centerName from local storage using key countryCode and centerName
            const tmpStorage = await chrome.storage.local.get(['countryCode', 'centerName']);
            countryCode = tmpStorage.countryCode || countryCode;
            centerName = tmpStorage.centerName || centerName;
            console.log("Using stored values for countryCode and centerName:", { countryCode, centerName });
            countryCode = countryCode ? countryCode : 'default';
            centerName = centerName ? centerName : 'default';
          }
          if (clientCode && countryCode && centerName && pageFormGroupId) {
            const urlParams = { clientCode, countryCode, centerName, formGroupId: pageFormGroupId };
            await chrome.storage.local.set({ [STORAGE_KEYS.URL_PARAMS_FOR_RULES]: urlParams });

            // --- Set currentAppointmentUrl only if not already set ---
            console.log(`Setting initial currentAppointmentUrl: ${cleanedUrl}`);
            currentAppointmentUrl = cleanedUrl; // Use the cleaned URL
            // --- End setting currentAppointmentUrl ---

            // --- Set Running State ---
            isRunning = true;
            appointmentInProgress = false; // Reset progress state on start
            updateIcon(true);
            console.log('Started monitoring appointment tab:', appointmentTabId, 'with params:', urlParams, 'Target URL:', currentAppointmentUrl);

            // --- Save State (including TabID and potentially new URL) ---
            await saveState(); // Save isRunning, appointmentTabId, currentAppointmentUrl etc.
            if (refreshInterval < 300) {
              if (currentAppointmentUrl.includes('visas-fr.')) {
                console.error('Refresh interval must be at least 5 minutes (300 seconds) for France tourist appointments.');
                // Reset state to stop monitoring
                await resetState();
                await sendRuntimeError(sendResponse, 'franceIntervalTooShort');
                return; // Stop processing here
              } else {
                console.warn('Non-France appointment detected. Interval can be less than 5 minutes.');
              }
            }
            // --- Setup Alarm and Rules ---
            await chrome.alarms.clear('refreshAppointment');
            chrome.alarms.create('refreshAppointment', {
              delayInMinutes: 1 / 60,
              periodInMinutes: refreshInterval / 60
            });
            console.log('Alarm created for refreshAppointment with period:', refreshInterval / 60, 'minutes');
            nextRefreshTime = Date.now() + (refreshInterval * 1000);

            await saveState();
            await setupRequestBlockingRules(urlParams); // Update rules based on new settings and params
            sendResponse({ success: true });
          } else {
            console.error("Could not parse necessary params from URL for DNR rules:", tabUrl);
            await resetState(); // This will also clear rules by calling setupRequestBlockingRules(null)
            await sendRuntimeError(sendResponse, 'startPageInstructions');
            return;
          }
        } else {
          console.log('Current tab is not a valid TLSContact appointment page:', tabs.length > 0 ? tabs[0].url : 'No active tab found');
          await resetState(); // Ensure stopped state if criteria not met
          await sendRuntimeError(sendResponse, 'invalidAppointmentPage');
        }
      } catch (error) {
        console.error('Error during startMonitoring:', error);
        await resetState();
        await sendRuntimeError(sendResponse, 'unexpectedError');
      }
      // No need for explicit return true; for async listeners

    } else if (message.action === 'stopMonitoring') {
      if (appointmentTabId) {
        sendTabMessage(appointmentTabId, { action: 'RemoveInterceptor' }, 'Error removing interceptor');
      }
      console.log('Processing stopMonitoring');
      await resetState(); // Use resetState to handle cleanup
      sendResponse({ success: true });

    } else if (message.action === 'getStatus') {
      // Ensure we send the latest state, especially after a potential restart
      await loadState(); // Load fresh state before responding
      sendResponse({
        isRunning: isRunning,
        interval: refreshInterval,
        tabId: appointmentTabId,
        appointmentInProgress: appointmentInProgress,
        allowPrimeSlots: allowPrimeSlots,
        startDate: startDate,
        startTime: startTime,
        endDate: endDate,
        endTime: endTime,
        dayOfWeek: dayOfWeek, // Added
        nextRefreshTime: isRunning && !appointmentInProgress ? nextRefreshTime : null, // Only provide next time if actively running & not paused
        ringtoneActive,
        appointmentDetails
      });

    } else if (message.action === 'getNextRefreshTime') {
      await loadState(); // Load fresh state
      if (isRunning && !appointmentInProgress && nextRefreshTime) {
        sendResponse({ nextRefreshTime: nextRefreshTime });
      } else if (isRunning && !appointmentInProgress) {
        // If running but time is null (e.g., after restart before first alarm), try getting from alarm
        const alarm = await chrome.alarms.get('refreshAppointment');
        if (alarm && alarm.scheduledTime) {
          nextRefreshTime = alarm.scheduledTime;
          await saveState(); // Save the recovered time
          sendResponse({ nextRefreshTime: nextRefreshTime });
        } else {
          // Still can't determine, send null
          sendResponse({ nextRefreshTime: null });
        }
      }
      else {
        sendResponse({ nextRefreshTime: null }); // Not running or appointment in progress
      }

    } else if (message.action === 'getAllowPrimeSlots') {
      await loadState(); // Load fresh state
      const licenseStatus = await verifyStoredLicense();
      sendResponse({
        allowPrimeSlots: allowPrimeSlots,
        startDate: startDate,
        startTime: startTime,
        endDate: endDate,
        endTime: endTime,
        dayOfWeek: dayOfWeek, // Added
        entitlementActive: licenseStatus.valid
      });

      // Handle content script messages
    } else if (message.action === 'appointmentFound') {
      console.log('Received appointmentFound message');
      if (isRunning && appointmentTabId) { // Only act if monitoring is supposed to be active
        appointmentInProgress = true;
        nextRefreshTime = null; // Pause countdown/refresh indication
        await saveState();
        await startRingtone({
          slotType: message.slotType || null,
          source: 'appointmentFound'
        });
        console.log('Appointment found, pausing page refreshes');
        updateIcon(true); // Keep icon running, maybe change later?
        sendResponse({ success: true });
      } else {
        console.warn('Received appointmentFound but not in a valid running state.');
        await sendRuntimeError(sendResponse, 'invalidRunningState');
      }


    } else if (message.action === 'appointmentPageInvalid') {
      console.log('Content script reports invalid appointment page, stopping monitoring');
      await resetState();
      sendResponse({ success: true });

    } else if (message.action === 'appointmentStatus') {
      await loadState(); // Ensure state is current before potentially resuming
      if (!isRunning || !appointmentTabId) {
        console.warn("Received appointmentStatus but not running or no tab ID.");
        await sendRuntimeError(sendResponse, 'notRunningState');
        return; // Don't proceed
      }

      if (message.confirmed) {
        console.log('Appointment confirmed successfully. Keeping monitoring paused.');
        // State remains: 
        appointmentInProgress = true
        nextRefreshTime = null
        // nextRefreshTime remains null. Icon stays running.
        // We don't resume automatically here. User must stop/start manually.
        await saveState(); // Save the confirmed 'in progress' state
        await startRingtone({
          slotType: message.slotType || null,
          datetime: message.datetime || null,
          source: 'appointmentStatus'
        });

        // Track appointment confirmed event
        // ... (existing tracking code - assumed okay) ...

      } else {
        console.log('Appointment confirmation failed or canceled, resuming monitoring');
        appointmentInProgress = false; // Resume checks

        // Resume refreshing - recreate alarm if necessary or let the existing one continue
        const alarm = await chrome.alarms.get('refreshAppointment');
        if (!alarm) {
          console.log("Alarm not found after appointment failure, recreating.");
          chrome.alarms.create('refreshAppointment', {
            delayInMinutes: 0.1, // Restart soon
            periodInMinutes: refreshInterval / 60
          });
          nextRefreshTime = Date.now() + (refreshInterval * 1000); // Estimate
        } else {
          // Alarm exists, calculate next refresh time based on its schedule
          nextRefreshTime = alarm.scheduledTime || (Date.now() + (refreshInterval * 1000));
          console.log("Resuming monitoring, next refresh based on existing alarm:", new Date(nextRefreshTime));
        }

        await saveState(); // Save resumed state (appointmentInProgress=false, updated nextRefreshTime)
        updateIcon(true); // Ensure icon is running
      }
      sendResponse({ success: true });

      // Handle tracking events from popup
    } else if (message.action === 'stopRingtone') {
      await stopRingtone();
      sendResponse({ success: true });

    } else if (message.action === 'trackEvent') {
      // console.log('Tracking event:', message.eventName, message.params);
      // Placeholder for actual tracking logic
      sendResponse({ success: true });

    } else if (message.action === 'getLoginDetailsAndAttemptLogin') {
      // Handle proactive request from content script on login page load
      await loadState(); // Ensure state is current
      if (userEmail && userPassword && currentAppointmentUrl) {
        console.log('Background responding to getLoginDetailsAndAttemptLogin with credentials.');
        sendResponse({
          success: true,
          email: userEmail,
          password: userPassword,
          redirectToUrl: currentAppointmentUrl
        });
      } else {
        console.log('Background responding to getLoginDetailsAndAttemptLogin - missing details.');
        sendResponse({ success: false });
      }
      // Note: No return true needed here as sendResponse is called synchronously within the async block

    } else {
      console.log("Received unknown message action:", message.action);
      // Optional: sendResponse({ success: false, error: 'Unknown action' });
    }
  })(); // Execute async IIFE

  return true; // Indicates async response REQUIRED for message listener
});

// Handle tab closing
chrome.tabs.onRemoved.addListener(async (tabId) => {
  // Load state first to ensure we're acting on the correct tab ID
  await loadState();
  if (tabId === appointmentTabId) {
    console.log(`Monitored tab ${tabId} closed.`);
    // Track tab closed event before resetting state
    // sendEvent('monitoring_tab_closed'); // Assuming sendEvent is defined elsewhere if needed
    await resetState(); // Reset state fully
  }
});


// Handle initial installation and loading settings/env vars
chrome.runtime.onInstalled.addListener(async (details) => {
  console.log('Extension installed or updated:', details.reason);
  await verifyStoredLicense();
  // Set the initial icon state (not running)
  updateIcon(false); // Set default icon initially

  // Load saved settings (interval, prime slots) if they exist, set defaults otherwise
  await loadState(); // Load existing state first

  // If state wasn't previously saved (e.g., first install), apply defaults now
  if (details.reason === 'install') {
    console.log('First install, ensuring default interval/prime slots are set.');
    allowPrimeSlots = true;
    // Ensure these defaults are saved
    await saveState();
  }

  await initializeExtensionRules();

  // After loading/setting initial state and env vars, update the icon based on potentially loaded state
  await setupRequestBlockingRules(); // Also call here to set rules on install/update
  updateIcon(isRunning);
});
