/**
 * GET /api/cron/brand-sitemap-crawl — daily brand sitemap refresh.
 *
 * Protected by CRON_SECRET. Crawls every brand's own sitemap so the
 * Page Launch Tracker can render sitemap-derived launch dates without
 * waiting for a user-triggered refresh. Brands with a cache younger
 * than RECRAWL_GUARD_HOURS are skipped.
 *
 * Mirrors the pacing of competitor-crawl: batches of 5 with a 2s gap,
 * stops at ~55s to stay under the 60s Vercel cron cap.
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import {
  crawlBrandSitemap,
  getCachedBrandSitemap,
} from "@/lib/services/brand-sitemap";

export const dynamic = "force-dynamic";
export const maxDuration = 300;

const CRON_SECRET = process.env.CRON_SECRET ?? "";
const BATCH_SIZE = 5;
const BATCH_DELAY_MS = 2000;
const RECRAWL_GUARD_HOURS = 20;
const RUNTIME_CAP_MS = 55_000;

function authOk(req: Request): boolean {
  if (!CRON_SECRET) return false;
  const header = req.headers.get("authorization") ?? "";
  if (header === `Bearer ${CRON_SECRET}`) return true;
  return new URL(req.url).searchParams.get("secret") === CRON_SECRET;
}

function sleep(ms: number) { return new Promise<void>((r) => setTimeout(r, ms)); }

export async function GET(req: Request) {
  if (!authOk(req)) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  const startedAt = Date.now();

  // All brands with a domain. We filter recently-crawled ones below
  // after reading each brand's cache; a single groupBy on SiteEvent
  // would require joining back to Brand so an in-loop check keeps the
  // query count modest.
  const brands = await prisma.brand.findMany({
    where: { domain: { not: "" }, isDemo: false },
    select: { id: true, domain: true },
  });

  const recrawlCutoffMs = Date.now() - RECRAWL_GUARD_HOURS * 3600_000;
  let crawled = 0;
  let skipped = 0;
  let failed = 0;

  for (let i = 0; i < brands.length; i += BATCH_SIZE) {
    if (Date.now() - startedAt > RUNTIME_CAP_MS) break;
    const batch = brands.slice(i, i + BATCH_SIZE);
    await Promise.all(batch.map(async (b) => {
      if (!b.domain) return;
      const cached = await getCachedBrandSitemap(b.id);
      if (cached && new Date(cached.crawledAt).getTime() > recrawlCutoffMs) {
        skipped++;
        return;
      }
      try {
        await crawlBrandSitemap(b.id, b.domain);
        crawled++;
      } catch (err) {
        failed++;
        console.error(`[cron/brand-sitemap-crawl] ${b.id} (${b.domain})`, err instanceof Error ? err.message : err);
      }
    }));
    if (i + BATCH_SIZE < brands.length) await sleep(BATCH_DELAY_MS);
  }

  return NextResponse.json({
    crawled, skipped, failed,
    durationMs: Date.now() - startedAt,
    totalBrands: brands.length,
  });
}
