/**
 * Lever public job postings API.
 *
 * https://github.com/lever/postings-api/blob/master/README.md
 *   GET https://api.lever.co/v0/postings/{company}?mode=json
 *
 * No auth. We iterate `leverCompanies` (set in Settings or LEVER_COMPANIES env)
 * and pull every active posting; downstream filtering by `searchTerms` /
 * country happens in the pipeline.
 */

import type {
  ExtractorManifest,
  ExtractorRunResult,
} from "@shared/types/extractors";
import type { CreateJobInput } from "@shared/types/jobs";

interface LeverCategories {
  team?: string;
  department?: string;
  commitment?: string;
  location?: string;
  allLocations?: string[];
}
interface LeverPosting {
  id?: string;
  text?: string;
  hostedUrl?: string;
  applyUrl?: string;
  description?: string;
  descriptionPlain?: string;
  categories?: LeverCategories;
  createdAt?: number;
  workplaceType?: string;
}

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

function readCompanies(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().toLowerCase() : "",
        )
        .filter((entry) => entry.length > 0);
    }
  } catch {
    // fall through to delimited-list parsing below
  }
  return raw
    .split(/[\n,;|]+/)
    .map((entry) => entry.trim().toLowerCase())
    .filter(Boolean);
}

function locationFor(posting: LeverPosting): string | undefined {
  const cats = posting.categories;
  if (!cats) return undefined;
  if (Array.isArray(cats.allLocations) && cats.allLocations.length > 0) {
    return cats.allLocations.filter(Boolean).join("; ");
  }
  return asString(cats.location);
}

function mapPosting(
  posting: LeverPosting,
  company: string,
): CreateJobInput | null {
  const jobUrl = asString(posting.hostedUrl);
  if (!jobUrl) return null;
  const employer = company
    .split("-")
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(" ");
  return {
    source: "lever",
    sourceJobId: asString(posting.id),
    title: asString(posting.text) ?? "Unknown Title",
    employer: employer || company,
    jobUrl,
    applicationLink: asString(posting.applyUrl) ?? jobUrl,
    location: locationFor(posting),
    jobType: asString(posting.categories?.commitment),
    jobFunction: asString(posting.categories?.team),
    companyIndustry: asString(posting.categories?.department),
    isRemote:
      posting.workplaceType?.toLowerCase() === "remote" ? true : undefined,
    datePosted:
      typeof posting.createdAt === "number"
        ? new Date(posting.createdAt).toISOString()
        : undefined,
    jobDescription:
      asString(posting.descriptionPlain) ?? asString(posting.description),
  };
}

async function fetchCompany(company: string): Promise<LeverPosting[]> {
  const url = `https://api.lever.co/v0/postings/${encodeURIComponent(company)}?mode=json`;
  const response = await fetch(url, {
    headers: { Accept: "application/json" },
  });
  if (response.status === 404) return [];
  if (!response.ok) {
    throw new Error(
      `Lever request for "${company}" failed with status ${response.status}`,
    );
  }
  const body = (await response.json()) as unknown;
  return Array.isArray(body) ? (body as LeverPosting[]) : [];
}

export const manifest: ExtractorManifest = {
  id: "lever",
  displayName: "Lever (ATS)",
  providesSources: ["lever"],
  async run(context): Promise<ExtractorRunResult> {
    if (context.shouldCancel?.()) return { success: true, jobs: [] };

    const companies = readCompanies(context.settings.leverCompanies);
    if (companies.length === 0) {
      return {
        success: true,
        jobs: [],
        error:
          "No Lever companies configured. Set LEVER_COMPANIES or the leverCompanies setting (comma- or newline-separated slugs).",
      };
    }

    const seen = new Set<string>();
    const out: CreateJobInput[] = [];

    try {
      for (let i = 0; i < companies.length; i += 1) {
        if (context.shouldCancel?.()) break;
        const company = companies[i];
        context.onProgress?.({
          phase: "list",
          termsProcessed: i,
          termsTotal: companies.length,
          currentUrl: company,
          detail: `Lever: ${company} (${i + 1}/${companies.length})`,
        });

        let added = 0;
        const postings = await fetchCompany(company);
        for (const posting of postings) {
          const mapped = mapPosting(posting, company);
          if (!mapped) continue;
          const key = mapped.sourceJobId || mapped.jobUrl;
          if (seen.has(key)) continue;
          seen.add(key);
          out.push(mapped);
          added += 1;
        }

        context.onProgress?.({
          phase: "list",
          termsProcessed: i + 1,
          termsTotal: companies.length,
          currentUrl: company,
          jobPagesProcessed: out.length,
          detail: `Lever: ${company} → ${added} jobs (${out.length} total)`,
        });
      }
    } catch (error) {
      const message = error instanceof Error ? error.message : "Unknown error";
      return { success: false, jobs: out, error: message };
    }

    return { success: true, jobs: out };
  },
};

export default manifest;
