import type { CreateJobInput } from "@shared/types/jobs";

export type AtsKind =
  | "ashby"
  | "greenhouse"
  | "lever"
  | "teamtailor"
  | "huntflow"
  | "factorial";

export interface DetectedAts {
  kind: AtsKind;
  slug: string;
}

export function readUrlList(raw: string | undefined): string[] {
  if (!raw) return [];
  try {
    const parsed = JSON.parse(raw);
    if (Array.isArray(parsed)) {
      return parsed
        .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
        .filter(Boolean);
    }
  } catch {
    // fall through
  }
  return raw
    .split(/[\n,;|]+/)
    .map((entry) => entry.trim())
    .filter(Boolean);
}

export function detectAtsTargets(html: string, pageUrl: string): DetectedAts[] {
  const found: DetectedAts[] = [];
  const seen = new Set<string>();

  const add = (kind: AtsKind, slug: string) => {
    const key = `${kind}:${slug.toLowerCase()}`;
    if (!slug || seen.has(key)) return;
    seen.add(key);
    found.push({ kind, slug });
  };

  for (const match of html.matchAll(/jobs\.ashbyhq\.com\/([a-zA-Z0-9_-]+)/g)) {
    add("ashby", match[1]);
  }
  for (const match of html.matchAll(
    /boards-api\.greenhouse\.io\/v1\/boards\/([a-zA-Z0-9_-]+)/g,
  )) {
    add("greenhouse", match[1]);
  }
  for (const match of html.matchAll(
    /boards\.greenhouse\.io\/([a-zA-Z0-9_-]+)/g,
  )) {
    add("greenhouse", match[1]);
  }
  for (const match of html.matchAll(/jobs\.lever\.co\/([a-zA-Z0-9_-]+)/g)) {
    add("lever", match[1]);
  }
  for (const match of html.matchAll(
    /https?:\/\/([a-z0-9-]+)\.teamtailor\.com/gi,
  )) {
    add("teamtailor", match[1]);
  }
  for (const match of html.matchAll(
    /https?:\/\/([a-z0-9-]+)\.huntflow\.io/gi,
  )) {
    add("huntflow", match[1]);
  }
  for (const match of html.matchAll(
    /https?:\/\/([a-z0-9-]+)\.factorialhr\.com/gi,
  )) {
    add("factorial", match[1]);
  }

  try {
    const host = new URL(pageUrl).hostname.toLowerCase();
    if (host.endsWith(".huntflow.io")) {
      add("huntflow", host.replace(/\.huntflow\.io$/, ""));
    }
    if (host.endsWith(".factorialhr.com")) {
      add("factorial", host.replace(/\.factorialhr\.com$/, ""));
    }
    if (host.endsWith(".teamtailor.com")) {
      add("teamtailor", host.replace(/\.teamtailor\.com$/, ""));
    }
  } catch {
    // ignore invalid pageUrl
  }

  return found;
}

function asString(value: unknown): string | undefined {
  if (typeof value !== "string") return undefined;
  const trimmed = value.trim();
  return trimmed ? trimmed : undefined;
}

function decodeHtmlEntities(value: string): string {
  return value
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'");
}

function stripHtml(html: string): string {
  return decodeHtmlEntities(html)
    .replace(/<[^>]+>/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

async function fetchAshby(slug: string): Promise<CreateJobInput[]> {
  const url = `https://api.ashbyhq.com/posting-api/job-board/${encodeURIComponent(slug)}`;
  const response = await fetch(url, {
    headers: { Accept: "application/json" },
  });
  if (!response.ok) return [];
  const body = (await response.json()) as {
    jobs?: Array<Record<string, unknown>>;
  };
  return (body.jobs ?? []).flatMap((job) => {
    const jobUrl = asString(job.jobUrl);
    if (!jobUrl) return [];
    return [
      {
        source: "careerspages" as const,
        sourceJobId: asString(job.id),
        title: asString(job.title) ?? "Unknown Title",
        employer: slug,
        jobUrl,
        applicationLink: asString(job.applyUrl) ?? jobUrl,
        location:
          asString(job.location) ?? asString(job.locationName) ?? "Unknown",
        isRemote: job.isRemote === true,
        datePosted: asString(job.publishedAt),
        jobDescription:
          asString(job.descriptionPlain) ??
          (job.descriptionHtml
            ? stripHtml(String(job.descriptionHtml))
            : undefined),
      },
    ];
  });
}

async function fetchGreenhouse(slug: string): Promise<CreateJobInput[]> {
  const url = `https://boards-api.greenhouse.io/v1/boards/${encodeURIComponent(slug)}/jobs?content=true`;
  const response = await fetch(url, {
    headers: { Accept: "application/json" },
  });
  if (!response.ok) return [];
  const body = (await response.json()) as {
    jobs?: Array<Record<string, unknown>>;
  };
  return (body.jobs ?? []).flatMap((job) => {
    const jobUrl = asString(job.absolute_url);
    if (!jobUrl) return [];
    const location = job.location as { name?: string } | undefined;
    return [
      {
        source: "careerspages" as const,
        sourceJobId: String(job.id ?? jobUrl),
        title: asString(job.title) ?? "Unknown Title",
        employer: slug,
        jobUrl,
        applicationLink: jobUrl,
        location: asString(location?.name) ?? "Unknown",
        datePosted: asString(job.updated_at),
        jobDescription: job.content
          ? stripHtml(String(job.content))
          : undefined,
      },
    ];
  });
}

async function fetchLever(slug: string): Promise<CreateJobInput[]> {
  const url = `https://api.lever.co/v0/postings/${encodeURIComponent(slug)}?mode=json`;
  const response = await fetch(url, {
    headers: { Accept: "application/json" },
  });
  if (!response.ok) return [];
  const body = (await response.json()) as Array<Record<string, unknown>>;
  if (!Array.isArray(body)) return [];
  return body.flatMap((job) => {
    const jobUrl = asString(job.hostedUrl) ?? asString(job.applyUrl);
    if (!jobUrl) return [];
    const categories = job.categories as Record<string, unknown> | undefined;
    return [
      {
        source: "careerspages" as const,
        sourceJobId: asString(job.id),
        title: asString(job.text) ?? "Unknown Title",
        employer: slug,
        jobUrl,
        applicationLink: asString(job.applyUrl) ?? jobUrl,
        location: asString(categories?.location) ?? "Unknown",
        datePosted: asString(job.createdAt),
        jobDescription: job.descriptionPlain
          ? String(job.descriptionPlain)
          : job.description
            ? stripHtml(String(job.description))
            : undefined,
      },
    ];
  });
}

function xmlText(xml: string, tag: string): string | undefined {
  const pattern = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`, "i");
  const match = xml.match(pattern);
  if (!match?.[1]) return undefined;
  return (
    match[1].replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, "$1").trim() || undefined
  );
}

async function fetchTeamtailor(slug: string): Promise<CreateJobInput[]> {
  const url = `https://${encodeURIComponent(slug)}.teamtailor.com/jobs.rss?per_page=200`;
  const response = await fetch(url, {
    headers: { Accept: "application/rss+xml, application/xml, text/xml" },
  });
  if (!response.ok) return [];
  const xml = await response.text();
  const out: CreateJobInput[] = [];
  for (const raw of xml.match(/<item>([\s\S]*?)<\/item>/gi) ?? []) {
    const block = raw.replace(/^<item>/i, "").replace(/<\/item>$/i, "");
    const jobUrl = xmlText(block, "link");
    if (!jobUrl) continue;
    out.push({
      source: "careerspages",
      sourceJobId: xmlText(block, "guid") ?? jobUrl,
      title: xmlText(block, "title") ?? "Unknown Title",
      employer: xmlText(block, "company_name") ?? slug,
      jobUrl,
      applicationLink: jobUrl,
      location: xmlText(block, "location") ?? "Unknown",
      datePosted: xmlText(block, "pubDate"),
      jobDescription: xmlText(block, "description")
        ? stripHtml(xmlText(block, "description") ?? "")
        : undefined,
    });
  }
  return out;
}

async function fetchHuntflow(slug: string): Promise<CreateJobInput[]> {
  const origin = `https://${slug}.huntflow.io`;
  const response = await fetch(`${origin}/`, {
    headers: { Accept: "text/html", "User-Agent": "JobOps/1.0" },
  });
  if (!response.ok) return [];
  const html = await response.text();
  const out: CreateJobInput[] = [];
  const pattern =
    /<article class="_item_[^"]*">[\s\S]*?<a href="(\/vacancy\/[^"]+)"[^>]*>([^<]+)<\/a>[\s\S]*?<div class="_info_[^"]*">([^<]*)<\/div>/g;
  for (const match of html.matchAll(pattern)) {
    const jobUrl = `${origin}${match[1]}`;
    out.push({
      source: "careerspages",
      sourceJobId: match[1]?.replace(/^\/vacancy\//, ""),
      title: decodeHtmlEntities(match[2]?.trim() ?? "Unknown Title"),
      employer: slug,
      jobUrl,
      applicationLink: jobUrl,
      location: match[3]?.trim() || "Unknown",
      isRemote: match[3]?.toLowerCase().includes("remote"),
    });
  }
  return out;
}

async function fetchFactorial(slug: string): Promise<CreateJobInput[]> {
  const origin = `https://${slug}.factorialhr.com`;
  const response = await fetch(origin, {
    headers: { Accept: "text/html", "User-Agent": "JobOps/1.0" },
  });
  if (!response.ok) return [];
  const html = await response.text();
  const out: CreateJobInput[] = [];
  for (const match of html.matchAll(
    /(?:href|data-job-postings-url)=['"]([^'"]*\/job_posting\/[^'"]+)['"]/g,
  )) {
    const raw = match[1];
    const path = raw.startsWith("http")
      ? raw.replace(origin, "")
      : raw.startsWith("/")
        ? raw
        : `/${raw}`;
    const jobUrl = `${origin}${path}`;
    const slugPart = path.split("/").pop() ?? "";
    const title = slugPart
      .replace(/-\d+$/, "")
      .split("-")
      .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
      .join(" ");
    out.push({
      source: "careerspages",
      sourceJobId: slugPart,
      title,
      employer: slug,
      jobUrl,
      applicationLink: jobUrl,
      location: "Unknown",
    });
  }
  return out;
}

export async function fetchJobsForTarget(
  target: DetectedAts,
): Promise<CreateJobInput[]> {
  switch (target.kind) {
    case "ashby":
      return fetchAshby(target.slug);
    case "greenhouse":
      return fetchGreenhouse(target.slug);
    case "lever":
      return fetchLever(target.slug);
    case "teamtailor":
      return fetchTeamtailor(target.slug);
    case "huntflow":
      return fetchHuntflow(target.slug);
    case "factorial":
      return fetchFactorial(target.slug);
  }
}

export async function fetchCareersPage(pageUrl: string): Promise<string> {
  const response = await fetch(pageUrl, {
    headers: { Accept: "text/html", "User-Agent": "JobOps/1.0" },
    redirect: "follow",
  });
  if (!response.ok) {
    throw new Error(
      `Careers page request failed (${response.status}) for ${pageUrl}`,
    );
  }
  return response.text();
}
