/**
 * Teamtailor public career sites — RSS feed per tenant.
 *
 * https://{company}.teamtailor.com/jobs.rss
 */

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

interface TeamtailorItem {
  title?: string;
  link?: string;
  guid?: string;
  description?: string;
  pubDate?: string;
  remoteStatus?: string;
  companyName?: string;
  location?: string;
  department?: 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() : ""))
        .filter(Boolean);
    }
  } catch {
    // fall through
  }
  return raw
    .split(/[\n,;|]+/)
    .map((entry) => entry.trim())
    .filter(Boolean);
}

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

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

function parseItems(xml: string): TeamtailorItem[] {
  const items: TeamtailorItem[] = [];
  const blocks = xml.match(/<item>([\s\S]*?)<\/item>/gi) ?? [];

  for (const raw of blocks) {
    const block = raw.replace(/^<item>/i, "").replace(/<\/item>$/i, "");
    items.push({
      title: xmlText(block, "title"),
      link: xmlText(block, "link"),
      guid: xmlText(block, "guid"),
      description: xmlText(block, "description"),
      pubDate: xmlText(block, "pubDate"),
      remoteStatus: xmlText(block, "remoteStatus"),
      companyName: xmlText(block, "company_name"),
      location: xmlText(block, "location"),
      department: xmlText(block, "department"),
    });
  }

  return items;
}

function matchesTerm(item: TeamtailorItem, term: string): boolean {
  const lower = term.toLowerCase();
  if (item.title?.toLowerCase().includes(lower)) return true;
  if (item.department?.toLowerCase().includes(lower)) return true;
  const description = item.description ? stripHtml(item.description) : "";
  if (description.toLowerCase().includes(lower)) return true;
  return false;
}

function mapJob(
  item: TeamtailorItem,
  companySlug: string,
): CreateJobInput | null {
  const jobUrl = asString(item.link);
  if (!jobUrl) return null;

  const employer =
    asString(item.companyName) ??
    companySlug
      .split(/[-_]/)
      .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
      .join(" ");

  const remoteStatus = asString(item.remoteStatus)?.toLowerCase();
  const isRemote =
    remoteStatus === "fully" ||
    remoteStatus === "hybrid" ||
    item.location?.toLowerCase().includes("remote") === true;

  return {
    source: "teamtailor",
    sourceJobId: item.guid ?? jobUrl,
    title: asString(item.title) ?? "Unknown Title",
    employer,
    jobUrl,
    applicationLink: jobUrl,
    location: asString(item.location) ?? "Unknown",
    isRemote,
    datePosted: asString(item.pubDate),
    jobDescription: item.description ? stripHtml(item.description) : undefined,
    jobFunction: asString(item.department),
  };
}

async function fetchCompanyFeed(company: string): Promise<TeamtailorItem[]> {
  const url = `https://${encodeURIComponent(company)}.teamtailor.com/jobs.rss?per_page=200`;
  const response = await fetch(url, {
    headers: {
      Accept: "application/rss+xml, application/xml, text/xml",
      "User-Agent": "JobOps/1.0",
    },
  });
  if (response.status === 404) return [];
  if (!response.ok) {
    throw new Error(
      `Teamtailor RSS for "${company}" failed with status ${response.status}`,
    );
  }
  return parseItems(await response.text());
}

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

    const companies = readCompanies(context.settings.teamtailorCompanies);
    if (companies.length === 0) {
      return {
        success: true,
        jobs: [],
        error:
          "No Teamtailor companies configured. Set TEAMTAILOR_COMPANIES or teamtailorCompanies (comma- or newline-separated subdomains).",
      };
    }

    const terms = context.searchTerms.length > 0 ? context.searchTerms : [];
    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: `Teamtailor: ${company} (${i + 1}/${companies.length})`,
        });

        const items = await fetchCompanyFeed(company);
        for (const item of items) {
          if (
            terms.length > 0 &&
            !terms.some((term) => matchesTerm(item, term))
          ) {
            continue;
          }
          const mapped = mapJob(item, company);
          if (!mapped) continue;
          const key = mapped.sourceJobId || mapped.jobUrl;
          if (seen.has(key)) continue;
          seen.add(key);
          out.push(mapped);
        }
      }

      return { success: true, jobs: out };
    } catch (error) {
      const message = error instanceof Error ? error.message : "Unknown error";
      return { success: false, jobs: out, error: message };
    }
  },
};

export default manifest;
