// Reddit platform adapter.
// Reddit uses a navigation-based flow (no modal): /r/<sub>/submit
// Each subreddit can have unique rules (karma minimums, flair requirements,
// post-frequency limits, etc.). Anti-spam is strict — slow posting required.

const RD_SELECTORS = {
  // Title field on submit page
  titleField: [
    'textarea[placeholder*="Title" i]',
    'input[placeholder*="Title" i]',
    'textarea[name="title"]',
    'faceplate-textarea-input[name="title"] textarea',
  ],
  // Body textarea
  bodyField: [
    'div[contenteditable="true"][role="textbox"]',
    'shreddit-composer textarea',
    'textarea[name="text"]',
    'div[data-name="markdown-body"] [contenteditable="true"]',
  ],
  // Submit/Post button
  submit: [
    'button[type="submit"]',
    'shreddit-submit-button button',
    'button[id*="submit" i]:not([disabled])',
  ],
  // "Post" type selector (Text / Link / Image)
  textTab: [
    '[aria-label*="Text post" i]', 'button[id*="text" i]',
  ],
  // File input for image/video
  fileInput: [
    'input[type="file"][accept*="image"]',
    'input[type="file"][accept*="video"]',
  ],
  // Flair button (some subs require flair)
  flairBtn: [
    'button[aria-label*="lair" i]', 'button[id*="flair" i]',
  ],
};

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 });
  });
}

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

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

// ---------- Typing ----------

function* typingPlan(text, cfg) {
  const min = cfg.typingMinMsPerChar ?? 45;
  const max = cfg.typingMaxMsPerChar ?? 150;
  for (const ch of text) {
    let d = min + Math.random() * (max - min);
    if (/[.,!?\n]/.test(ch)) d *= 2;
    yield { ch, delayMs: Math.floor(d) };
  }
}

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(300);
  for (const { ch, delayMs } of typingPlan(text, cfg)) {
    dispatchKeyChar(el, ch);
    await sleep(delayMs);
  }
}

// ---------- Reddit browsing simulation ----------

async function humanlikeRedditBrowse(minSec, maxSec) {
  const totalMs = (minSec + Math.random() * (maxSec - minSec)) * 1000;
  const start = Date.now();
  reportStep('info', `👤 Reddit browse for ${Math.round(totalMs / 1000)}s`);
  while (Date.now() - start < totalMs) {
    window.scrollBy({ top: 400 + Math.random() * 600, behavior: 'smooth' });
    await sleep(2000 + Math.random() * 3500);
  }
  window.scrollTo({ top: 0, behavior: 'smooth' });
  await sleep(1000);
}

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

function detectRedditWarning() {
  const patterns = [
    { kind: 'rate-limit', regex: /you are doing that too much|try again in|rate limit/i },
    { kind: 'rate-limit', regex: /אתה עושה זאת יותר מדי/i },
    { kind: 'karma-required', regex: /you don'?t have enough karma|minimum karma|insufficient karma/i },
    { kind: 'account-too-new', regex: /your account is too new|account age requirement|account must be at least/i },
    { kind: 'flair-required', regex: /flair is required|please add a flair|requires post flair/i },
    { kind: 'banned-from-sub', regex: /you are banned from|banned from posting/i },
    { kind: 'sub-rules', regex: /this post violates|removed by automoderator|read the rules/i },
    { kind: 'captcha', regex: /are you a human|captcha|please prove/i },
  ];
  const scope = [
    ...document.querySelectorAll('[role="alert"], [role="dialog"]'),
    ...document.querySelectorAll('[id*="error" i], [class*="error" i], [class*="banner" 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, title, media = [], throttle = {} }) {
  reportStep('info', `📍 Reddit publish on ${location.pathname}`);
  await sleep(2000);

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

  // 0) Pre-browse to look like an active redditor
  const browseMin = throttle.preBrowseMinSec ?? 20;
  const browseMax = throttle.preBrowseMaxSec ?? 60;
  if (browseMin > 0) await humanlikeRedditBrowse(browseMin, browseMax);

  // 1) Make sure we're on /submit
  if (!location.pathname.includes('/submit')) {
    reportStep('warn', 'Not on /submit, navigating');
    // The URL should already be /r/<sub>/submit by the SW
    return { ok: false, error: 'must be on /submit page' };
  }

  // 2) Title field (Reddit requires a title — use the post title or first 100 chars of text)
  const titleText = title || (text || '').split('\n')[0].slice(0, 290);
  reportStep('info', `Title: "${titleText.slice(0, 60)}..."`);
  const titleEl = await waitFor(RD_SELECTORS.titleField, { timeout: 8000 }).catch(() => null);
  if (!titleEl) return { ok: false, error: 'title field not found' };
  await typeIntoField(titleEl, titleText, throttle);
  await sleep(800 + Math.random() * 1200);

  // 3) Body (optional)
  if (text && text.length > titleText.length) {
    const bodyEl = querySelectorAny(RD_SELECTORS.bodyField);
    if (bodyEl) {
      reportStep('info', `Body: ${text.length} chars`);
      await typeIntoField(bodyEl, text, throttle);
      await sleep(800);
    }
  }

  // 4) Media (if any)
  if (media && media.length) {
    reportStep('info', `Media: ${media.length} files`);
    try { await attachRedditMedia(media); }
    catch (e) { reportStep('warn', `Media failed: ${e.message}`); }
  }

  // 5) Check for required flair (best-effort — user may need to set one manually)
  const flairBtn = querySelectorAny(RD_SELECTORS.flairBtn);
  if (flairBtn && /required|חובה/i.test(document.body.innerText.slice(0, 3000))) {
    reportStep('warn', '⚠ This subreddit may require flair — manual review recommended');
  }

  // 6) Submit
  await sleep(randBetween(1000, 2500));
  let submit = querySelectorAny(RD_SELECTORS.submit);
  if (!submit) return { ok: false, error: 'submit not found' };
  const start = Date.now();
  while (submit.disabled || submit.getAttribute('aria-disabled') === 'true') {
    if (Date.now() - start > 10000) return { ok: false, error: 'submit disabled' };
    await sleep(400);
    submit = querySelectorAny(RD_SELECTORS.submit) || submit;
  }
  reportStep('info', '✓ Clicking Submit');
  submit.click();

  // 7) Outcome — Reddit navigates to the post page on success
  const startUrl = location.href;
  for (let i = 0; i < 30; i++) {
    await sleep(500);
    if (location.href !== startUrl && /\/comments\//.test(location.href)) {
      reportStep('info', `✅ Posted — ${location.href}`);
      return { ok: true, signal: 'navigated-to-post', url: location.href };
    }
    const warn2 = detectRedditWarning();
    if (warn2) return { ok: false, warning: warn2 };
  }

  // Could be slower — check for warning or assume success
  const warn3 = detectRedditWarning();
  if (warn3) return { ok: false, warning: warn3 };
  return { ok: true, signal: 'optimistic-no-failure', url: location.href };
}

async function attachRedditMedia(mediaList) {
  const files = mediaList.map((m) => new File([new Uint8Array(m.buffer)], m.name, { type: m.mimeType }));
  const input = querySelectorAny(RD_SELECTORS.fileInput);
  if (!input) throw new Error('file input not found');
  const dt = new DataTransfer();
  for (const f of files) dt.items.add(f);
  input.files = dt.files;
  input.dispatchEvent(new Event('change', { bubbles: true }));
  await sleep(3000);
}

// ---------- Engagement scraping ----------

function scrapePostEngagement() {
  // Reddit shows: upvotes ratio, comment count, share count, etc.
  const out = { reactions: 0, comments: 0, shares: 0, views: null };
  // Upvotes are in a span near the upvote button
  const upvoteText = document.querySelector('[id*="vote-arrows" i] span, [aria-label*="pvotes" i]')?.innerText || '';
  out.reactions = parseRedditCount(upvoteText);
  const commentText = document.querySelector('[id*="comments" i], a[href*="/comments/"]')?.innerText || '';
  out.comments = parseRedditCount(commentText);
  return out;
}

function parseRedditCount(s) {
  if (!s) return 0;
  const match = s.match(/([\d.,]+\s*[kKמM]?)/);
  if (!match) return 0;
  let v = match[1].trim().replace(/,/g, '');
  if (/k|K/i.test(v)) return Math.round(parseFloat(v) * 1000) || 0;
  if (/m|M/i.test(v)) return Math.round(parseFloat(v) * 1000000) || 0;
  return parseInt(v, 10) || 0;
}

// ---------- Comment harvest (leads pipeline) ----------
// Reddit's new UI (sh.reddit.com) uses <shreddit-comment> custom elements
// nested under <shreddit-comment-tree>. Old reddit uses <div class="comment">.
async function harvestComments({ maxComments = 200 } = {}) {
  // Click "View more" / expand if present
  const expand = Array.from(document.querySelectorAll('button')).find(b =>
    /view all comments|show more|view \d+ more/i.test(b.innerText || ''));
  if (expand) { try { expand.click(); await sleep(1500); } catch {} }
  // Multi-pass scroll
  for (let i = 0; i < 6; i++) {
    window.scrollBy({ top: 700, behavior: 'smooth' });
    await sleep(900);
  }
  const out = [];
  const seen = new Set();
  // New Reddit
  const newComments = document.querySelectorAll('shreddit-comment');
  for (const c of newComments) {
    const author = c.getAttribute('author') || c.querySelector('faceplate-tracker[noun="user_profile"] a')?.innerText?.trim();
    if (!author) continue;
    const textEl = c.querySelector('[id$="-comment-rtjson-content"], [slot="comment"] p, div[id^="t1_"] div p');
    const text = (textEl?.innerText || c.querySelector('[slot="comment"]')?.innerText || '').trim().slice(0, 1500);
    if (!text || text.length < 2) continue;
    const authorUrl = `https://www.reddit.com/user/${author}/`;
    const key = author + '::' + text.slice(0, 60);
    if (seen.has(key)) continue;
    seen.add(key);
    out.push({ author, authorUrl, text, timestamp: Date.now() });
    if (out.length >= maxComments) break;
  }
  // Old Reddit fallback
  if (out.length === 0) {
    const oldComments = document.querySelectorAll('div.comment');
    for (const c of oldComments) {
      const author = c.querySelector('a.author')?.innerText?.trim();
      if (!author) continue;
      const text = (c.querySelector('div.usertext-body, .md')?.innerText || '').trim().slice(0, 1500);
      if (!text) continue;
      const authorUrl = `https://www.reddit.com/user/${author}/`;
      const key = author + '::' + text.slice(0, 60);
      if (seen.has(key)) continue;
      seen.add(key);
      out.push({ author, authorUrl, text, timestamp: Date.now() });
      if (out.length >= maxComments) break;
    }
  }
  return out;
}

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

// ─────────────────────────────────────────────────────────────────
// SEARCH_ENGAGE — open subreddit, upvote N hot posts (no commenting
// in v1 — Reddit anti-spam is the strictest of all platforms).
// Caller arrives on /r/<sub>/hot or /search?q=<kw> and we scroll a
// bit, pick top non-pinned posts, click their upvote arrows.
// ─────────────────────────────────────────────────────────────────
async function redditSearchEngage({ maxUpvotes = 3, skipAuthors = [], minBodyChars = 60 } = {}) {
  const results = { upvoted: [], skipped: [], errors: [] };
  // Wait for posts to render
  await new Promise(r => setTimeout(r, 1800));
  // Scroll a bit so more posts are visible
  for (let i = 0; i < 3; i++) {
    window.scrollBy(0, 500);
    await new Promise(r => setTimeout(r, 700));
  }

  // Reddit's modern UI uses <shreddit-post> elements; old reddit has .thing.link
  const posts = Array.from(document.querySelectorAll(
    'shreddit-post, article, [data-testid="post-container"], .Post'
  )).slice(0, 15);

  for (const post of posts) {
    if (results.upvoted.length >= maxUpvotes) break;
    try {
      // Skip pinned / promoted posts
      const txt = (post.textContent || '').toLowerCase();
      if (txt.includes('promoted') || txt.includes('stickied')) {
        results.skipped.push('promoted/stickied'); continue;
      }
      // Get author (best-effort)
      const authorEl = post.querySelector('a[href*="/user/"], shreddit-post-author, [data-testid="author"]');
      const author = authorEl?.textContent?.trim().replace(/^u\//, '') || '';
      if (author && skipAuthors.includes(author.toLowerCase())) {
        results.skipped.push(`skip ${author}`); continue;
      }
      // Body length sanity check (avoid empty/joke posts)
      const body = post.querySelector('[id$="-post-rtjson-content"], .md, .post-content')?.textContent || '';
      if (body.length < minBodyChars && !post.querySelector('img, video')) {
        results.skipped.push('too short'); continue;
      }
      // Find the upvote button
      const upvoteBtn = post.querySelector(
        'shreddit-upvote-button button[upvote], button[aria-label*="upvote" i], button[aria-pressed="false"][data-click-id="upvote"], button[data-redditstyle="true"][title*="vote" i]'
      ) || post.querySelector('shreddit-upvote-button button:first-child');
      if (!upvoteBtn) { results.skipped.push('no upvote btn'); continue; }
      // Check it's not already upvoted (Reddit toggles)
      if (upvoteBtn.getAttribute('aria-pressed') === 'true' || upvoteBtn.classList.contains('upvoted')) {
        results.skipped.push('already upvoted'); continue;
      }
      upvoteBtn.click();
      results.upvoted.push({ author, snippet: body.slice(0, 80) });
      // Human delay between actions: 2.5-5 sec
      await new Promise(r => setTimeout(r, 2500 + Math.random() * 2500));
    } catch (e) {
      results.errors.push(String(e?.message || e).slice(0, 100));
    }
  }
  return { ok: true, results };
}

chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
  (async () => {
    try {
      if (msg.type === 'PUBLISH_POST') {
        const p = msg.payload || {};
        // For Reddit we use the post.title field explicitly
        return sendResponse(await publishPost({ ...p, title: p.title || (p.text || '').split('\n')[0].slice(0, 290) }));
      }
      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 === 'SEARCH_ENGAGE') {
        const result = await redditSearchEngage(msg.payload || {});
        return sendResponse(result);
      }
      if (msg.type === 'DEBUG_PROBE') {
        return sendResponse({
          ok: true, platform: 'reddit', 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-rd] content script loaded on', location.pathname);
