// Instagram platform adapter.
// Web composer (added 2024-25) supports: single photo, carousel, Reel.
// Stories from web are still limited; we treat them best-effort.
//
// Critical: Instagram REQUIRES at least one media file. Text-only posts are
// not supported on web. The publish flow:
//   1. Click "Create" (+ icon)
//   2. Pick "Select from computer"
//   3. Upload file(s) via the hidden file input
//   4. "Next" through aspect-ratio / filter / advanced screens
//   5. Add caption + tags + location
//   6. Click "Share"

const IG_SELECTORS = {
  // Create button — the "+" icon. Multiple possible locations.
  createBtn: [
    'svg[aria-label*="New post" i]',
    'svg[aria-label*="פוסט חדש"]',
    'svg[aria-label*="Create" i]',
    'a[href="/create/select/"]',
    'button[aria-label*="Create" i]',
  ],
  // Dialog after clicking Create
  dialog: '[role="dialog"]',
  // "Select from computer" button inside the dialog
  selectFromComputer: [
    'button:not(:disabled)',  // generic — we'll filter by text
  ],
  // Hidden file input
  fileInput: 'input[type="file"][accept*="image"], input[type="file"][accept*="video"], [role="dialog"] input[type="file"]',
  // Next button (multiple steps before share)
  nextBtn: [
    'button:not(:disabled)',  // generic — filter by text "Next" / "הבא"
  ],
  // Caption textarea (last step)
  captionField: [
    '[role="dialog"] textarea[aria-label*="aption" i]',
    '[role="dialog"] textarea[aria-label*="כיתוב"]',
    '[role="dialog"] [contenteditable="true"][role="textbox"]',
    '[role="dialog"] textarea',
  ],
  // Share button (final)
  shareBtn: [
    'button:not(:disabled)',  // filter by text "Share" / "שיתוף"
  ],
};

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

function querySelectorAny(selectors, root = document) {
  const arr = Array.isArray(selectors) ? selectors : [selectors];
  for (const sel of arr) {
    try {
      const el = root.querySelector(sel);
      if (el) return el;
    } catch {}
  }
  return null;
}

function waitFor(selectors, { timeout = 15000, root = document } = {}) {
  return new Promise((resolve, reject) => {
    const start = Date.now();
    const found = querySelectorAny(selectors, root);
    if (found) return resolve(found);
    const observer = new MutationObserver(() => {
      const el = querySelectorAny(selectors, root);
      if (el) { observer.disconnect(); resolve(el); }
      else if (Date.now() - start > timeout) {
        observer.disconnect();
        reject(new Error('Timeout: ' + JSON.stringify(selectors)));
      }
    });
    observer.observe(document.body, { childList: true, subtree: true });
  });
}

// Find a button by its visible text (Instagram doesn't always use aria-label)
function findButtonByText(texts, root = document) {
  const buttons = root.querySelectorAll('button, [role="button"]');
  const lowered = texts.map((t) => t.toLowerCase());
  for (const btn of buttons) {
    const t = (btn.innerText || btn.textContent || '').trim().toLowerCase();
    if (!t || t.length > 40) continue;
    if (lowered.some((needle) => t === needle || t.startsWith(needle))) return btn;
  }
  return null;
}

function reportStep(level, message, meta = {}) {
  console.log(`[reachlab-ig][${level}] ${message}`, meta);
  try {
    chrome.runtime.sendMessage({ type: 'CONTENT_LOG', payload: { level, message, meta, platform: 'instagram' } });
  } catch {}
}

function randBetween(min, max) {
  return Math.floor(min + Math.random() * (max - min));
}

// ---------- Typing (for captions) ----------

function dispatchKeyChar(el, ch) {
  const keyCode = ch.length === 1 ? ch.charCodeAt(0) : 0;
  const opts = {
    key: ch, code: `Key${ch.toUpperCase()}`,
    keyCode, which: keyCode,
    bubbles: true, cancelable: true, composed: true,
  };
  el.dispatchEvent(new KeyboardEvent('keydown', opts));
  el.dispatchEvent(new InputEvent('beforeinput', {
    inputType: 'insertText', data: ch,
    bubbles: true, cancelable: true, composed: true,
  }));
  const ok = document.execCommand && document.execCommand('insertText', false, ch);
  if (!ok) {
    el.dispatchEvent(new InputEvent('input', {
      inputType: 'insertText', data: ch,
      bubbles: true, cancelable: true, composed: true,
    }));
  }
  el.dispatchEvent(new KeyboardEvent('keyup', opts));
}

async function typeIntoField(el, text, cfg) {
  el.focus();
  el.click();
  await sleep(400);
  const min = cfg.typingMinMsPerChar ?? 50;
  const max = cfg.typingMaxMsPerChar ?? 130;
  for (const ch of text) {
    dispatchKeyChar(el, ch);
    let d = min + Math.random() * (max - min);
    if (/[.,!?\n]/.test(ch)) d *= 1.8;
    await sleep(Math.floor(d));
  }
}

// ---------- Browsing simulation ----------

async function humanlikeInstagramBrowse(minSec, maxSec) {
  const totalMs = (minSec + Math.random() * (maxSec - minSec)) * 1000;
  const start = Date.now();
  reportStep('info', `👤 IG browse for ${Math.round(totalMs / 1000)}s`);
  while (Date.now() - start < totalMs) {
    window.scrollBy({ top: 300 + Math.random() * 500, behavior: 'smooth' });
    await sleep(1800 + Math.random() * 3000);
  }
  window.scrollTo({ top: 0, behavior: 'smooth' });
  await sleep(1000);
}

// ---------- Warning detection ----------

function detectInstagramWarning() {
  const patterns = [
    { kind: 'action-blocked', regex: /action blocked|we restrict certain activity|try again later/i },
    { kind: 'action-blocked', regex: /פעולה נחסמה|מגביל פעילויות מסוימות|נסה שוב/i },
    { kind: 'rate-limit', regex: /you've reached the limit|too many requests/i },
    { kind: 'rate-limit', regex: /הגעת למגבלה|יותר מדי בקשות/i },
    { kind: 'captcha', regex: /verify it'?s you|security check|prove you'?re not a bot/i },
    { kind: 'captcha', regex: /אמת שזה אתה|בדיקת אבטחה/i },
    { kind: 'suspended', regex: /your account has been disabled|temporarily locked/i },
    { kind: 'suspended', regex: /החשבון שלך הושבת|נעול זמנית/i },
    { kind: 'content-rejected', regex: /this content goes against|removed for violating/i },
    { kind: 'content-rejected', regex: /התוכן מפר|הוסר בגלל הפרת/i },
  ];
  const scope = [
    ...document.querySelectorAll('[role="dialog"], [role="alert"]'),
    ...document.querySelectorAll('[class*="error" i], [class*="warn" i]'),
  ];
  for (const el of scope) {
    const text = (el.innerText || '').slice(0, 1000);
    if (!text) continue;
    for (const p of patterns) {
      if (p.regex.test(text)) return { kind: p.kind, message: text.slice(0, 200), pageUrl: location.href };
    }
  }
  return null;
}

// ---------- Publish flow ----------

async function publishPost({ text, media = [], throttle = {} }) {
  reportStep('info', `📍 Instagram publish on ${location.pathname}`);
  await sleep(2000);

  // Instagram REQUIRES media on web
  if (!media || !media.length) {
    reportStep('error', '⚠ אינסטגרם דורש תמונה או וידאו — פוסט טקסט בלבד לא נתמך');
    return { ok: false, error: 'אינסטגרם דורש תמונה/וידאו — הוסף מדיה לפוסט' };
  }

  const warn = detectInstagramWarning();
  if (warn) {
    reportStep('error', `⚠ IG warning before start: ${warn.kind}`);
    return { ok: false, warning: warn };
  }

  // Pre-browse
  const browseMin = throttle.preBrowseMinSec ?? 25;
  const browseMax = throttle.preBrowseMaxSec ?? 70;
  if (browseMin > 0) await humanlikeInstagramBrowse(browseMin, browseMax);

  // 1) Click Create button
  reportStep('info', 'Looking for Create button...');
  let createBtn = querySelectorAny(IG_SELECTORS.createBtn);
  if (createBtn) {
    // The svg is inside a clickable parent
    createBtn = createBtn.closest('button, a, [role="button"]') || createBtn;
  }
  if (!createBtn) {
    reportStep('error', '❌ Create button not found');
    return { ok: false, error: 'Create button not found' };
  }
  await sleep(randBetween(800, 2000));
  createBtn.click();

  // 2) Wait for dialog + "Select from computer" button
  await waitFor(IG_SELECTORS.dialog, { timeout: 8000 });
  await sleep(1500);
  reportStep('info', 'Dialog opened');

  // 3) Find file input — sometimes the dialog shows "Select from computer"
  // which is just a label that triggers the file input
  let fileInput = document.querySelector(IG_SELECTORS.fileInput);
  if (!fileInput) {
    // Click "Select from computer" / "בחרו מהמחשב"
    const selectBtn = findButtonByText(['select from computer', 'בחרו מהמחשב', 'select from device']);
    if (selectBtn) {
      selectBtn.click();
      await sleep(800);
    }
    fileInput = document.querySelector(IG_SELECTORS.fileInput);
  }
  if (!fileInput) {
    reportStep('error', '❌ File input not found');
    return { ok: false, error: 'file input not found' };
  }

  // 4) Upload files
  reportStep('info', `Uploading ${media.length} file(s)...`);
  const files = media.map((m) => new File([new Uint8Array(m.buffer)], m.name, { type: m.mimeType }));
  const dt = new DataTransfer();
  for (const f of files) dt.items.add(f);
  fileInput.files = dt.files;
  fileInput.dispatchEvent(new Event('change', { bubbles: true }));
  await sleep(4000);  // wait for upload + preview

  // 5) Click Next (crop/aspect ratio screen)
  let nextBtn = findButtonByText(['next', 'הבא']);
  if (nextBtn) {
    reportStep('info', '✓ Clicking Next (aspect ratio)');
    nextBtn.click();
    await sleep(2500);
  }

  // 6) Click Next (filter/edit screen)
  nextBtn = findButtonByText(['next', 'הבא']);
  if (nextBtn) {
    reportStep('info', '✓ Clicking Next (filter)');
    nextBtn.click();
    await sleep(2500);
  }

  // 7) Caption — find caption field, type text
  if (text) {
    const captionEl = querySelectorAny(IG_SELECTORS.captionField);
    if (captionEl) {
      reportStep('info', `Typing caption (${text.length} chars)`);
      // Caption max 2200 chars on Instagram
      const safeText = text.slice(0, 2200);
      await typeIntoField(captionEl, safeText, throttle);
      await sleep(800);
    } else {
      reportStep('warn', '⚠ Caption field not found — posting without caption');
    }
  }

  // 8) Click Share
  await sleep(randBetween(1000, 2500));
  const shareBtn = findButtonByText(['share', 'שיתוף', 'post']);
  if (!shareBtn) {
    reportStep('error', '❌ Share button not found');
    return { ok: false, error: 'share button not found' };
  }
  if (shareBtn.disabled || shareBtn.getAttribute('aria-disabled') === 'true') {
    return { ok: false, error: 'share button disabled' };
  }
  reportStep('info', '✓ Clicking Share');
  shareBtn.click();

  // 9) Detect outcome — IG shows "Your post has been shared" or closes the dialog
  const startUrl = location.href;
  for (let i = 0; i < 30; i++) {
    await sleep(800);
    const warn2 = detectInstagramWarning();
    if (warn2) return { ok: false, warning: warn2 };
    // Success: dialog gone OR a "shared" confirmation appears
    if (!document.querySelector(IG_SELECTORS.dialog)) {
      reportStep('info', '✅ Dialog closed — published');
      return { ok: true, signal: 'dialog-closed', url: location.href };
    }
    const successText = document.body.innerText.slice(0, 1500);
    if (/your post has been shared|הפוסט שותף/i.test(successText)) {
      return { ok: true, signal: 'shared-confirmation', url: location.href };
    }
  }
  return { ok: true, signal: 'optimistic-no-failure', url: location.href };
}

// ---------- Engagement scraping (limited on web) ----------

function scrapePostEngagement() {
  const out = { reactions: 0, comments: 0, shares: 0, views: null };
  const text = document.body.innerText.slice(0, 3000);
  const m1 = text.match(/([\d,.]+\s*[KMכ]?)\s*(?:likes?|לייקים)/i);
  if (m1) out.reactions = parseIGCount(m1[1]);
  const m2 = text.match(/(?:view\s+)?(?:all\s+)?([\d,.]+\s*[KMכ]?)\s*comments?/i);
  if (m2) out.comments = parseIGCount(m2[1]);
  const m3 = text.match(/([\d,.]+\s*[KMכ]?)\s*views?/i);
  if (m3) out.views = parseIGCount(m3[1]);
  return out;
}

function parseIGCount(s) {
  if (!s) return 0;
  s = s.trim().replace(/,/g, '');
  if (/K|כ/i.test(s)) return Math.round(parseFloat(s) * 1000) || 0;
  if (/M/i.test(s)) return Math.round(parseFloat(s) * 1000000) || 0;
  return parseInt(s, 10) || 0;
}

// ---------- Comment harvest (leads pipeline) ----------
// Instagram post comments: each is a <ul> > <li> with author link + text.
// Anti-pattern: IG aggressively rate-limits scraping. Keep dwell long + caps low.
async function harvestComments({ maxComments = 100 } = {}) {
  // Click "View all N comments" to expand
  const expand = Array.from(document.querySelectorAll('span,button')).find(el =>
    /view all .* comments|הצג את כל .* התגובות/i.test(el.innerText || ''));
  if (expand) { try { expand.click(); await sleep(1500); } catch {} }
  // Repeatedly click "Load more comments" (plus icon button)
  for (let i = 0; i < 4; i++) {
    const loadMore = document.querySelector('button[aria-label*="Load more comments" i], button svg[aria-label*="Load more comments" i]');
    if (!loadMore) break;
    try { (loadMore.closest('button') || loadMore).click(); await sleep(1800); } catch { break; }
  }
  // Scroll inside the comments pane (if modal) or page
  for (let i = 0; i < 5; i++) {
    window.scrollBy({ top: 500, behavior: 'smooth' });
    await sleep(900);
  }
  // Each comment is typically a UL > LI containing an <a href="/<handle>/"> and the text
  const items = document.querySelectorAll('ul ul li, ul > li');
  const out = [];
  const seen = new Set();
  for (const li of items) {
    const authorLink = li.querySelector('a[href^="/"][role="link"], a[href^="/"]:not([href*="/p/"])');
    if (!authorLink) continue;
    const href = authorLink.getAttribute('href') || '';
    const m = href.match(/^\/([^/]+)\/?$/);
    if (!m) continue;
    const handle = m[1];
    if (handle.startsWith('p') || handle.startsWith('explore') || handle.length < 2) continue;
    const text = (li.innerText || '').trim().split('\n').slice(1).join(' ').slice(0, 1500);
    if (!text || text.length < 2) continue;
    const authorUrl = `https://www.instagram.com/${handle}/`;
    const key = handle + '::' + text.slice(0, 60);
    if (seen.has(key)) continue;
    seen.add(key);
    out.push({ author: handle, authorUrl, text, timestamp: Date.now() });
    if (out.length >= maxComments) break;
  }
  return out;
}

// ---------- Message handler ----------

chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
  (async () => {
    try {
      if (msg.type === 'PUBLISH_POST') {
        return sendResponse(await publishPost(msg.payload || {}));
      }
      if (msg.type === 'FIND_POST_URL') {
        return sendResponse({ ok: true, url: location.href });
      }
      if (msg.type === 'SCRAPE_POST_ENGAGEMENT') {
        return sendResponse({ ok: true, data: scrapePostEngagement() });
      }
      if (msg.type === 'HARVEST_COMMENTS') {
        const comments = await harvestComments(msg.payload || {});
        return sendResponse({ ok: true, comments });
      }
      if (msg.type === 'DEBUG_PROBE') {
        return sendResponse({
          ok: true, platform: 'instagram', url: location.href, pathname: location.pathname,
        });
      }
      sendResponse({ ok: false, error: 'unknown message type' });
    } catch (e) {
      sendResponse({ ok: false, error: String(e?.message || e) });
    }
  })();
  return true;
});

console.log('[reachlab-ig] content script loaded on', location.pathname);
