import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";

const source = readFileSync(new URL("./content-linkedin.js", import.meta.url), "utf8");
const start = source.indexOf('"competitor-siphon":');
const end = source.indexOf('"accept-invites-http":', start);
const siphon = source.slice(start, end);

test("competitor siphon requests activity with a real employee profile reference", () => {
  assert.match(siphon, /profileUrn: emp\.entityUrn \|\| emp\.linkedinUrl \|\| emp\.navigationUrl/);
  assert.match(siphon, /actResult\?\.success === false/);
  assert.match(
    siphon,
    /readLinkedInProfilePostsViaWorker\([\s\S]*emp\.publicIdentifier,[\s\S]*engage: canEngageRendered/,
  );
});

test("competitor siphon does not stop merely because no external engagers were exposed", () => {
  assert.doesNotMatch(siphon, /if \(allEngagers\.length === 0\).*return/);
  assert.match(siphon, /using discovered employees as the connection fallback/);
  assert.match(siphon, /if \(depth !== "light"\) \{\s*const connectionPool =/);
  assert.match(siphon, /sendVerifiedConnection/);
  assert.match(siphon, /connectionConfirmed/);
  assert.match(siphon, /Sent and verified Pending/);
});

test("competitor siphon likes unique source posts and counts only accepted reactions", () => {
  assert.match(siphon, /seenPostUrns\.has\(post\.urn\)/);
  assert.match(siphon, /const likeBudget = depth === "light"/);
  assert.doesNotMatch(siphon, /Heavy \(like \+ comment/);
  assert.match(siphon, /for \(const sourcePost of prioritizedSourcePosts\)/);
  assert.match(siphon, /if \(result\?\.success === true\) \{\s*liked\+\+/);
  assert.doesNotMatch(siphon, /await linkedinCmd\("linkedin:http-like-post"[^;]+;\s*liked\+\+/);
  assert.match(siphon, /let liked = renderedLiked/);
  assert.match(siphon, /if \(sourcePost\.liked\) continue/);
  assert.match(siphon, /LinkedIn API confirmed, post \.\.\.\$\{postRef\}/);
  assert.match(siphon, /Siphon complete: \$\{liked\} confirmed likes/);
  assert.doesNotMatch(siphon, /No employee posts were available to like; no actions were taken/);
});

test("competitor siphon exposes progress and limits slow rendered-page scans by depth", () => {
  assert.match(siphon, /Math\.min\(10, Math\.max\(5, maxActions \* 2\)\)/);
  assert.match(siphon, /Scanning employee \$\{employeeIndex \+ 1\}\/\$\{employeesToScan\.length\}/);
  assert.match(siphon, /if \(isCancelled\(\)\)/);
  assert.match(
    siphon,
    /Employee activity API failed for two consecutive employees; using rendered pages/,
  );
  assert.match(siphon, /internal error fetching data from downstream/i);
  assert.match(siphon, /consecutiveActivityApiFailures >= 2/);
  assert.match(siphon, /consecutiveActivityApiFailures = 0/);
  assert.match(siphon, /Siphon stopped before any further LinkedIn actions/);
  assert.match(
    siphon,
    /No likeable company or employee posts were exposed; continuing with employee connection fallback/,
  );
});

test("competitor siphon uses company posts before sampling employee activity", () => {
  assert.match(siphon, /linkedin:http-company-activity/);
  assert.match(siphon, /companyId: company\.companyId/);
  assert.match(
    siphon,
    /await collectPostEngagers\(companyPosts, company\.name \|\| slug, "company"\)/,
  );
  assert.match(siphon, /const employeesToScan = sampledEmployees/);
  assert.match(siphon, /also sampling employee activity so employee posts are not skipped/);
  assert.match(siphon, /prioritizedSourcePosts/);
  assert.ok(
    siphon.indexOf('sourceKind !== "company"') < siphon.indexOf('sourceKind === "company"'),
    "employee-authored posts must receive the first Like-budget slots",
  );
  assert.match(siphon, /depth === "light" \|\| postEngagersBroken/);
});

test("competitor siphon can continue when employee discovery is unavailable", () => {
  assert.match(
    siphon,
    /Employee discovery: \$\{empResult\.error\}\. Company posts can still be engaged/,
  );
  assert.match(siphon, /No employees discoverable; continuing with company posts/);
  assert.doesNotMatch(siphon, /No employees discoverable"\); return/);
});

test("rendered profile-post fallback uses the dedicated LinkedIn worker tab", () => {
  const background = readFileSync(new URL("./background.js", import.meta.url), "utf8");
  const start = background.indexOf('msg.action === "li-read-profile-posts-bg"');
  const end = background.indexOf("// r50", start);
  const handler = background.slice(start, end);

  assert.match(handler, /getOrCreateWorkerTab\(\)/);
  assert.match(handler, /recent-activity\/all\//);
  assert.match(handler, /"li:scan-profile-posts"/);
  assert.match(handler, /engage: msg\.engage === true/);
  assert.doesNotMatch(handler, /getLinkedInTab\(sender/);
});

test("zero-card employee activity scans upload route-aware diagnostics", () => {
  assert.match(source, /selector_missing:profile_activity_posts/);
  assert.match(source, /pageUrl: window\.location\.href/);
  assert.match(source, /pageTitle: document\.title/);
});

test("rendered profile likes return an auditable post receipt and real confirmation path", () => {
  assert.match(
    source,
    /likedPostUrn: likeResult\?\.success === true \? likeCandidate\?\.urn \|\| null : null/,
  );
  assert.match(siphon, /domResult\.likePath === "voyager"/);
  assert.match(
    siphon,
    /Liked \$\{employeeName\}'s rendered post \[\$\{confirmation\}\$\{postRef\}\]/,
  );
});

test("rendered profile reruns preserve and report already-liked post state", () => {
  assert.match(source, /liked: candidate\.liked === true/);
  assert.match(
    source,
    /alreadyLiked: candidates\.filter\(\(candidate\) => candidate\.liked\)\.length/,
  );
  assert.match(siphon, /renderedAlreadyLiked = Number\(domResult\.alreadyLiked\) \|\| 0/);
  assert.match(siphon, /renderedAlreadyLiked >= renderedPostCount/);
  assert.match(siphon, /rendered recent posts are already liked; skipping employee/);
  assert.match(siphon, /Math\.min\(10, Math\.max\(5, maxActions \* 2\)\)/);
});
