// OLX Parser — Background Service Worker
// Phase 2 (tab-based phone extraction) runs here so it survives popup close/open

let isRunning = false;
let workerTabId = null;

// ── Keep service worker alive during active parse ──────────────────
chrome.alarms.onAlarm.addListener(alarm => {
  if (alarm.name === 'keepalive' && isRunning) {
    chrome.alarms.create('keepalive', { delayInMinutes: 0.4 });
  }
});

function keepAliveStart() {
  chrome.alarms.create('keepalive', { delayInMinutes: 0.4 });
}

function keepAliveStop() {
  chrome.alarms.clear('keepalive');
}

// ── Send message to popup (silently ignore if popup is closed) ─────
function toPopup(msg) {
  chrome.runtime.sendMessage(msg).catch(() => {});
}

// ── Save progress to storage so popup can restore on reopen ───────
async function saveProgress(listings, currentIndex, results, timerStart, total, speed) {
  await chrome.storage.local.set({
    parseSession: {
      listings,
      currentIndex,
      results,
      total,
      timerStarted: timerStart,
      speed: speed || 'normal',
      active: true
    }
  });
}

async function clearProgress() {
  await chrome.storage.local.remove('parseSession');
}

// ── Persistent "done" URLs across sessions ─────────────────────────
async function getDoneUrls() {
  const data = await chrome.storage.local.get('olxDoneUrls');
  return new Set(data.olxDoneUrls || []);
}

async function markUrlDone(url) {
  const done = await getDoneUrls();
  done.add(url);
  await chrome.storage.local.set({ olxDoneUrls: [...done] });
}

async function clearDoneUrls() {
  await chrome.storage.local.remove('olxDoneUrls');
}

// ── Message router ─────────────────────────────────────────────────
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.type === 'START_PHASE2') {
    if (!isRunning) {
      runTabPhase(msg.listings, msg.categoryUrl, msg.startIndex || 0, msg.timerStart || Date.now(), msg.results || [], msg.trialMode, msg.trialUsed, msg.speed || 'normal');
    }
    sendResponse({ ok: true });
    return true;
  }

  if (msg.type === 'CLEAR_DONE_URLS') {
    clearDoneUrls().then(() => sendResponse({ ok: true }));
    return true;
  }

  if (msg.type === 'GET_DONE_COUNT') {
    getDoneUrls().then(done => sendResponse({ count: done.size }));
    return true;
  }

  if (msg.type === 'STOP_PHASE2') {
    isRunning = false;
    keepAliveStop();
    clearProgress();
    if (workerTabId) {
      chrome.tabs.remove(workerTabId).catch(() => {});
      workerTabId = null;
    }
    sendResponse({ ok: true });
    return true;
  }

  // Relay content script messages to popup
  sendResponse({ received: true });
  return true;
});

// ── Phase 2 main loop ──────────────────────────────────────────────
const DELAYS = {
  safe:   { min: 20000, max: 45000, mid5: [90000, 180000],  mid10: [240000, 480000] },
  normal: { min: 8000,  max: 20000, mid5: [60000, 90000],   mid10: [120000, 240000] },
  fast:   { min: 3000,  max: 6000,  mid5: null,              mid10: null },
};

async function runTabPhase(listings, categoryUrl, startIndex, timerStart, existingResults, trialMode, trialUsed, speed = 'normal') {
  isRunning = true;
  keepAliveStart();

  // Filter out already-processed URLs (cross-session deduplication)
  const doneUrls = await getDoneUrls();
  const skipped = listings.filter(l => doneUrls.has(l.url)).length;
  listings = listings.filter(l => !doneUrls.has(l.url));
  if (skipped > 0) {
    toPopup({ type: 'PHASE2_LOG', text: `⏭ Пропущено ${skipped} вже оброблених оголошень`, level: 'info' });
  }
  if (listings.length === 0) {
    toPopup({ type: 'PHASE2_LOG', text: `✅ Всі оголошення вже оброблені раніше`, level: 'ok' });
    const elapsed = Math.floor((Date.now() - timerStart) / 1000);
    const elapsedStr = `${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, '0')}`;
    toPopup({ type: 'PHASE2_DONE', count: existingResults.length, elapsed: elapsedStr });
    isRunning = false;
    keepAliveStop();
    return;
  }

  const total = listings.length;
  const results = [...existingResults];

  await saveProgress(listings, startIndex, results, timerStart, total, speed);

  // Open reusable worker tab from the OLX category page (sets proper referrer)
  // Remember the user's current active tab to restore focus after each listing
  let userTabId = null;
  try {
    const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
    userTabId = activeTab?.id || null;
  } catch (_) {}

  try {
    // Open worker tab as ACTIVE so OLX sees visibilityState='visible' from the start
    const workerTab = await chrome.tabs.create({ url: categoryUrl || 'https://www.olx.ua/', active: true });
    workerTabId = workerTab.id;
    await waitForTabLoad(workerTabId);
    // Immediately return focus to the user's original tab
    if (userTabId) chrome.tabs.update(userTabId, { active: true }).catch(() => {});
  } catch (e) {
    toPopup({ type: 'PHASE2_LOG', text: `⚠️ Не вдалось відкрити вкладку: ${e.message}`, level: 'error' });
    isRunning = false;
    keepAliveStop();
    return;
  }

  for (let i = startIndex; i < listings.length; i++) {
    if (!isRunning) break;

    const listing = listings[i];
    const num = i + 1;

    toPopup({ type: 'PHASE2_STATUS', message: `Обробляю ${num} / ${total}: завантажую оголошення...` });

    // Verify worker tab still exists, recreate if needed
    try {
      await chrome.tabs.get(workerTabId);
    } catch (_) {
      try {
        const newTab = await chrome.tabs.create({ url: categoryUrl || 'https://www.olx.ua/', active: true });
        workerTabId = newTab.id;
        await waitForTabLoad(workerTabId);
        if (userTabId) chrome.tabs.update(userTabId, { active: true }).catch(() => {});
      } catch (e) {
        toPopup({ type: 'PHASE2_LOG', text: `⚠️ Не вдалось відновити вкладку: ${e.message}`, level: 'error' });
        break;
      }
    }

    const data = await extractFromListingTab(listing.url, workerTabId, userTabId);

    if (!isRunning) break;

    const result = {
      name: data?.name || 'Не вказано',
      phone: data?.phone || 'Не вказано',
      adType: data?.isBusiness ? 'Бізнес' : 'Приватне',
      url: listing.url
    };

    results.push(result);
    await markUrlDone(listing.url);

    // Trial check
    if (trialMode && result.phone !== 'Не вказано') {
      trialUsed++;
      await chrome.storage.local.set({ trialUsed });
      if (trialUsed >= 100) {
        isRunning = false;
        toPopup({ type: 'PHASE2_RESULT', result, num, total, trialUsed });
        toPopup({ type: 'PHASE2_TRIAL_EXHAUSTED' });
        await saveProgress(listings, i + 1, results, timerStart, total, speed);
        break;
      }
    }

    await saveProgress(listings, i + 1, results, timerStart, total);

    // Log result
    if (data?.blocked) {
      toPopup({ type: 'PHASE2_LOG', text: `🛑 OLX виявив підозрілу активність — зупиняю парсинг. Зачекайте 30-60 хв.`, level: 'error' });
      isRunning = false;
      break;
    } else if (result.phone && result.phone !== 'Не вказано') {
      toPopup({ type: 'PHASE2_LOG', text: `#${num} ${result.name} — ✅ ${result.phone}`, level: 'ok' });
    } else {
      const reason = data?.hadButton === false ? 'немає кнопки (чат)' : 'клік не розкрив номер';
      toPopup({ type: 'PHASE2_LOG', text: `#${num} ${result.name} — ${reason}`, level: data?.hadButton === false ? 'info' : 'warn' });
    }

    toPopup({ type: 'PHASE2_RESULT', result, num, total, trialUsed });

    // Delay based on selected speed mode
    if (i + 1 < listings.length && isRunning) {
      const processed = i - startIndex + 1;
      const d = DELAYS[speed] || DELAYS.normal;
      let delay;

      if (d.mid10 && processed % 10 === 0) {
        delay = d.mid10[0] + Math.random() * (d.mid10[1] - d.mid10[0]);
        toPopup({ type: 'PHASE2_LOG', text: `☕ Велика пауза після ${processed} оголошень (${Math.round(delay/60000)} хв)`, level: 'info' });
      } else if (d.mid5 && processed % 5 === 0) {
        delay = d.mid5[0] + Math.random() * (d.mid5[1] - d.mid5[0]);
        toPopup({ type: 'PHASE2_LOG', text: `⏸ Пауза відпочинку ${Math.round(delay/1000)}с`, level: 'info' });
      } else {
        delay = d.min + Math.random() * (d.max - d.min);
        if (speed !== 'fast' && Math.random() < 0.12) delay += 20000 + Math.random() * 20000;
      }

      toPopup({ type: 'PHASE2_STATUS', message: `Пауза ${Math.round(delay / 1000)}с перед наступним...` });
      await sleep(delay);
    }
  }

  // Cleanup
  if (workerTabId) {
    chrome.tabs.remove(workerTabId).catch(() => {});
    workerTabId = null;
  }

  if (isRunning) {
    // Finished naturally
    const elapsed = Math.floor((Date.now() - timerStart) / 1000);
    const elapsedStr = `${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, '0')}`;
    await clearProgress();
    toPopup({ type: 'PHASE2_DONE', count: results.length, elapsed: elapsedStr });
  } else {
    // Stopped by user — keep progress in storage for resume
    toPopup({ type: 'PHASE2_STOPPED', count: results.length });
  }

  isRunning = false;
  keepAliveStop();
}

// ── Extract phone from listing (navigate worker tab) ───────────────
async function extractFromListingTab(url, tabId, userTabId) {
  try {
    // Navigate as active tab so visibilityState = 'visible' during load
    await chrome.tabs.update(tabId, { url, active: true });
    await waitForTabLoad(tabId);
    // Return focus to user's tab immediately after load
    if (userTabId) chrome.tabs.update(userTabId, { active: true }).catch(() => {});

    const [res] = await chrome.scripting.executeScript({
      target: { tabId },
      func: async () => {
        const sleep = ms => new Promise(r => setTimeout(r, ms));
        const rand = (min, max) => min + Math.random() * (max - min);

        // Override visibility — OLX checks this for bot detection
        try {
          Object.defineProperty(document, 'hidden', { get: () => false, configurable: true });
          Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true });
        } catch (_) {}

        const cookieBtn = document.querySelector('[data-cy="dismiss-cookies-overlay"]');
        if (cookieBtn) { cookieBtn.click(); await sleep(rand(300, 600)); }

        // Simulate human reading the listing:
        // 1. Pause at top (reading title & price)
        await sleep(rand(1000, 2500));

        // 2. Scroll through photos area slowly
        const photoArea = document.querySelector('[data-cy="ad-photos"]') || document.querySelector('[data-testid="swiper-wrapper"]');
        if (photoArea) {
          photoArea.scrollIntoView({ behavior: 'smooth', block: 'center' });
          await sleep(rand(800, 1800));
        }

        // 3. Scroll down to read description (in chunks, not instantly)
        const steps = Math.floor(rand(3, 6));
        for (let s = 0; s < steps; s++) {
          window.scrollBy({ top: rand(100, 250), behavior: 'smooth' });
          await sleep(rand(400, 900));
        }

        // Collect all VISIBLE phone buttons (there's one near top and one near bottom of listing)
        const allBtns = [
          ...document.querySelectorAll('[data-cy="ad-contact-phone"]'),
          ...document.querySelectorAll('[data-testid="show-phone"]'),
        ].filter(b => b.getBoundingClientRect().width > 0);

        // Sort by vertical position: first = top button, last = bottom button
        allBtns.sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);

        // Randomly pick top or bottom button (like a real user)
        const pickBottom = allBtns.length > 1 && Math.random() > 0.5;
        const phoneBtn = pickBottom ? allBtns[allBtns.length - 1] : allBtns[0];

        const fireClick = (btn) => {
          const rect = btn.getBoundingClientRect();
          const cx = rect.left + rect.width / 2 + rand(-5, 5);
          const cy = rect.top + rect.height / 2 + rand(-3, 3);
          const opts = { bubbles: true, cancelable: true, view: window, clientX: cx, clientY: cy };
          for (const type of ['pointerover','mouseover','pointermove','mousemove','pointerdown','mousedown','pointerup','mouseup','click']) {
            btn.dispatchEvent(new MouseEvent(type, opts));
          }
        };

        const phoneRevealed = () => {
          if (document.querySelector('a[href^="tel:"]')) return true;
          const txt = document.querySelector('[data-testid="phones-container"]')?.textContent || '';
          return txt.length > 0 && !txt.includes('xxx');
        };

        if (phoneBtn) {
          // If bottom button chosen — scroll down first (like reading the listing), then scroll to button
          if (pickBottom) {
            window.scrollTo({ top: document.body.scrollHeight * rand(0.4, 0.7), behavior: 'smooth' });
            await sleep(rand(800, 1500));
          }
          phoneBtn.scrollIntoView({ block: 'center', behavior: 'smooth' });
          await sleep(rand(600, 1200));

          for (let attempt = 0; attempt < 3; attempt++) {
            fireClick(phoneBtn);
            for (let w = 0; w < 25; w++) {
              await sleep(200);
              if (phoneRevealed()) break;
            }
            if (phoneRevealed()) break;
            await sleep(rand(1500, 2500));
          }
        }

        const telEl = document.querySelector('a[href^="tel:"]');
        const phone = telEl?.textContent?.trim() || null;
        const name = document.querySelector('[data-testid="user-profile-user-name"]')?.textContent?.trim() || null;
        const isBusiness = !!(document.querySelector('[data-testid="user-profile-business-badge"]') || document.querySelector('[data-cy="seller-badge"]'));

        // Detect OLX suspicious activity block
        const pageText = document.body?.innerText || '';
        const blocked = pageText.includes('підозрілу активність') || pageText.includes('suspicious activity');

        return { phone, name, isBusiness, hadButton: !!phoneBtn, blocked };
      }
    });

    return res?.result || null;
  } catch (e) {
    toPopup({ type: 'PHASE2_LOG', text: `⚠️ Помилка: ${e?.message?.slice(0, 60)}`, level: 'warn' });
    return null;
  }
}

// ── Utilities ──────────────────────────────────────────────────────
function sleep(ms) {
  return new Promise(r => setTimeout(r, ms));
}

function waitForTabLoad(tabId, timeout = 25000) {
  return new Promise(resolve => {
    const timer = setTimeout(resolve, timeout);
    const listener = (id, info) => {
      if (id === tabId && info.status === 'complete') {
        clearTimeout(timer);
        chrome.tabs.onUpdated.removeListener(listener);
        setTimeout(resolve, 3000); // wait for React hydration
      }
    };
    chrome.tabs.onUpdated.addListener(listener);
  });
}
