import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { launchOptions } from "camoufox-js";
import { firefox } from "playwright";

const __dirname = dirname(fileURLToPath(import.meta.url));
const OUTPUT_PATH = join(__dirname, "../storage/jobs.json");

interface ScrapedJob {
  title: string;
  employer: string;
  jobUrl: string;
  location?: string;
  isRemote?: boolean;
}

function slugifyTerm(term: string): string {
  return term
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
}

function parseTerms(raw: string | undefined): string[] {
  if (!raw) return ["software-engineer"];
  try {
    const parsed = JSON.parse(raw) as unknown;
    if (Array.isArray(parsed)) {
      return parsed.map((entry) => slugifyTerm(String(entry))).filter(Boolean);
    }
  } catch {
    // fall through
  }
  return raw
    .split(/[\n|,]+/)
    .map((entry) => slugifyTerm(entry))
    .filter(Boolean);
}

function buildSearchUrl(roleSlug: string): string {
  return `https://wellfound.com/role/l/${encodeURIComponent(roleSlug)}/remote`;
}

async function scrapeRole(
  roleSlug: string,
  maxJobs: number,
): Promise<ScrapedJob[]> {
  const browser = await firefox.launch(await launchOptions({ headless: true }));
  const page = await browser.newPage();

  try {
    await page.goto(buildSearchUrl(roleSlug), {
      waitUntil: "domcontentloaded",
      timeout: 90_000,
    });
    await page.waitForTimeout(4_000);

    const jobs = await page.evaluate(() => {
      const results: Array<{
        title: string;
        employer: string;
        jobUrl: string;
        location?: string;
      }> = [];

      const scripts = Array.from(
        document.querySelectorAll('script[type="application/ld+json"]'),
      );
      for (const script of scripts) {
        try {
          const data = JSON.parse(script.textContent ?? "null") as unknown;
          const rows = Array.isArray(data) ? data : [data];
          for (const row of rows) {
            if (!row || typeof row !== "object") continue;
            const record = row as Record<string, unknown>;
            if (record["@type"] !== "JobPosting") continue;
            const title = String(record.title ?? "").trim();
            const jobUrl = String(record.url ?? record.sameAs ?? "").trim();
            const org = record.hiringOrganization as
              | Record<string, unknown>
              | undefined;
            const employer = String(org?.name ?? "Unknown Employer").trim();
            const location = String(
              (record.jobLocation as Record<string, unknown> | undefined)
                ?.name ??
                record.jobLocation ??
                "",
            ).trim();
            if (!title || !jobUrl) continue;
            results.push({
              title,
              employer,
              jobUrl,
              location: location || undefined,
            });
          }
        } catch {
          // ignore malformed JSON-LD blocks
        }
      }

      if (results.length === 0) {
        for (const anchor of Array.from(
          document.querySelectorAll('a[href*="/job/"], a[href*="/jobs/"]'),
        )) {
          const href = anchor.getAttribute("href") ?? "";
          if (!href.includes("/job")) continue;
          const jobUrl = href.startsWith("http")
            ? href
            : `https://wellfound.com${href.startsWith("/") ? href : `/${href}`}`;
          const title = (anchor.textContent ?? "").trim();
          if (!title || title.length < 4) continue;
          results.push({
            title,
            employer: "Unknown Employer",
            jobUrl,
          });
        }
      }

      return results;
    });

    const seen = new Set<string>();
    const out: ScrapedJob[] = [];
    for (const job of jobs) {
      if (out.length >= maxJobs) break;
      if (seen.has(job.jobUrl)) continue;
      seen.add(job.jobUrl);
      out.push({
        ...job,
        isRemote: true,
      });
    }

    return out;
  } finally {
    await browser.close();
  }
}

async function main(): Promise<void> {
  const terms = parseTerms(process.env.WELLFOUND_SEARCH_TERMS);
  const maxJobs = Number.parseInt(process.env.WELLFOUND_MAX_JOBS ?? "50", 10);
  const cap = Number.isFinite(maxJobs)
    ? Math.max(1, Math.min(maxJobs, 200))
    : 50;

  const all: ScrapedJob[] = [];
  const seen = new Set<string>();

  for (const term of terms) {
    const rows = await scrapeRole(term, cap);
    for (const row of rows) {
      if (all.length >= cap) break;
      if (seen.has(row.jobUrl)) continue;
      seen.add(row.jobUrl);
      all.push(row);
    }
  }

  await mkdir(dirname(OUTPUT_PATH), { recursive: true });
  await writeFile(OUTPUT_PATH, JSON.stringify(all, null, 2), "utf-8");
}

main().catch((error) => {
  console.error(error instanceof Error ? error.message : String(error));
  process.exit(1);
});
