/**
 * GET /api/cron/competitor-crawl — daily competitor sitemap monitoring.
 *
 * Protected by CRON_SECRET. Crawls active competitors that haven't been
 * crawled in the last 20 hours, in batches of 5 with a 2-second gap
 * between batches. Stops if the runtime approaches 55 seconds (Vercel
 * cron timeout is 60s) so remaining competitors are picked up next run.
 *
 * Generates Signals Center events for notable activity (>= 3 new pages,
 * >= 5 removed or updated pages).
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { crawlCompetitor } from "@/lib/services/competitor-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; // stop before 60s Vercel timeout

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

  // Only crawl competitors not crawled in the last 20 hours
  const recrawlCutoff = new Date(Date.now() - RECRAWL_GUARD_HOURS * 3600_000);
  const competitors = await prisma.competitor.findMany({
    where: {
      isActive: true,
      OR: [
        { lastCrawledAt: null },
        { lastCrawledAt: { lt: recrawlCutoff } },
      ],
    },
    select: { id: true, brandId: true, domain: true },
    orderBy: { lastCrawledAt: "asc" }, // oldest-crawled first
  });

  type ResultRow = { domain: string; ok: boolean; totalPages?: number; newPages?: number; updatedPages?: number; removedPages?: number; error?: string; skipped?: string };
  const results: ResultRow[] = [];
  let skippedForTime = 0;

  // Process in batches of BATCH_SIZE
  for (let i = 0; i < competitors.length; i += BATCH_SIZE) {
    // Runtime guard — stop before we hit the timeout
    if (Date.now() - startedAt > RUNTIME_CAP_MS) {
      skippedForTime = competitors.length - i;
      console.log(`[cron/competitor-crawl] Runtime cap reached after ${Math.round((Date.now() - startedAt) / 1000)}s — skipping ${skippedForTime} competitors`);
      break;
    }

    const batch = competitors.slice(i, i + BATCH_SIZE);

    for (const comp of batch) {
      // Per-competitor runtime check too
      if (Date.now() - startedAt > RUNTIME_CAP_MS) {
        skippedForTime++;
        continue;
      }

      try {
        const result = await crawlCompetitor(comp.id);
        results.push({
          domain: comp.domain,
          ok: true,
          totalPages: result.totalPages,
          newPages: result.newPages.length,
          updatedPages: result.updatedPages.length,
          removedPages: result.removedPages.length,
        });

        // Generate signals for notable changes
        if (result.newPages.length >= 3) {
          await prisma.siteEvent.create({
            data: {
              brandId: comp.brandId, pageUrl: comp.domain, eventType: "competitor_new_pages",
              eventData: {
                competitor: comp.domain,
                count: result.newPages.length,
                pages: result.newPages.slice(0, 10),
                title: `${comp.domain} published ${result.newPages.length} new pages`,
              } as object,
            },
          });
        }
        if (result.removedPages.length >= 5) {
          await prisma.siteEvent.create({
            data: {
              brandId: comp.brandId, pageUrl: comp.domain, eventType: "competitor_removed_pages",
              eventData: {
                competitor: comp.domain,
                count: result.removedPages.length,
                title: `${comp.domain} removed ${result.removedPages.length} pages — possible restructure`,
              } as object,
            },
          });
        }
        if (result.updatedPages.length >= 5) {
          await prisma.siteEvent.create({
            data: {
              brandId: comp.brandId, pageUrl: comp.domain, eventType: "competitor_updated_pages",
              eventData: {
                competitor: comp.domain,
                count: result.updatedPages.length,
                pages: result.updatedPages.slice(0, 10),
                title: `${comp.domain} updated ${result.updatedPages.length} pages`,
              } as object,
            },
          });
        }
      } catch (err) {
        console.error(`[cron/competitor-crawl] failed for ${comp.domain}`, err);
        results.push({ domain: comp.domain, ok: false, error: String(err).slice(0, 200) });
      }
    }

    // Pause between batches (but not after the last one)
    if (i + BATCH_SIZE < competitors.length) await sleep(BATCH_DELAY_MS);
  }

  const elapsed = Math.round((Date.now() - startedAt) / 1000);
  console.log("[cron/competitor-crawl] Complete", {
    eligible: competitors.length,
    crawled: results.length,
    succeeded: results.filter((r) => r.ok).length,
    failed: results.filter((r) => !r.ok).length,
    skippedForTime,
    elapsedSeconds: elapsed,
  });

  return NextResponse.json({ crawled: results.length, skippedForTime, elapsedSeconds: elapsed, results });
}
