import type { MetadataRoute } from "next";

/**
 * Drop-in `app/sitemap.ts` default export. Fetches the canonical sitemap
 * served by Page Zero at `/api/v1/public/sitemap.xml?key=...`, parses
 * the XML, and returns Next.js's `MetadataRoute.Sitemap` shape so the
 * framework can build the static sitemap at request time.
 *
 *   ```ts
 *   // app/sitemap.ts
 *   export { default } from "@soloworks/smking-next/sitemap";
 *   ```
 *
 * One line of customer code; everything else lives here. The SaaS endpoint
 * derives URLs from `site_pages` (sitemap → wc_api → homepage_crawl →
 * product_content → manual → sdk_traffic discovery tiers) so even sites
 * with no upstream sitemap end up with a complete inventory.
 *
 * Customer can opt out by setting `SMKING_DISABLE_TAKEOVER_SITEMAP=1` —
 * the helper returns an empty array, letting any subsequent sitemap
 * source (e.g. next-sitemap) take over without crashing the build.
 *
 * Required env:
 *   - `SMKING_API_KEY`        (publishable, the `pk_…` key)
 *   - `SMKING_BASE_URL`       (defaults to https://getpagezero.com)
 */
export default async function smkingSitemap(): Promise<MetadataRoute.Sitemap> {
  if (process.env.SMKING_DISABLE_TAKEOVER_SITEMAP === "1") return [];
  const apiKey = process.env.SMKING_API_KEY;
  if (!apiKey) return [];
  const baseUrl = process.env.SMKING_BASE_URL ?? "https://getpagezero.com";

  let body: string;
  try {
    const res = await fetch(
      `${baseUrl}/api/v1/public/sitemap.xml?key=${encodeURIComponent(apiKey)}`,
      {
        // Daily revalidation — sitemap doesn't change second-to-second.
        // Customer can override via env if their build cadence differs.
        next: { revalidate: 86_400 },
        signal: AbortSignal.timeout(10_000),
      },
    );
    if (!res.ok) return [];
    body = await res.text();
  } catch {
    return [];
  }

  return parseSitemapEntries(body);
}

/**
 * Pure XML → MetadataRoute.Sitemap conversion. Lives outside the default
 * export so the unit test can drive it without a fetch mock.
 */
export function parseSitemapEntries(xml: string): MetadataRoute.Sitemap {
  const entries: MetadataRoute.Sitemap = [];
  const urlBlockRe = /<url>([\s\S]*?)<\/url>/g;
  const locRe = /<loc>([^<]+)<\/loc>/;
  const lastmodRe = /<lastmod>([^<]+)<\/lastmod>/;

  let match: RegExpExecArray | null;
  while ((match = urlBlockRe.exec(xml)) !== null) {
    const block = match[1];
    const loc = block.match(locRe)?.[1]?.trim();
    if (!loc) continue;
    const lastmodStr = block.match(lastmodRe)?.[1]?.trim();
    const lastModified = lastmodStr ? new Date(lastmodStr) : undefined;
    entries.push({
      url: decodeXmlEntities(loc),
      ...(lastModified && !Number.isNaN(lastModified.getTime())
        ? { lastModified }
        : {}),
    });
  }
  return entries;
}

function decodeXmlEntities(s: string): string {
  return s
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&apos;/g, "'");
}
