/**
 * GET /api/cron/signal-detection
 * ────────────────────────────────
 * Every 6 hours, walk every brand with an installed Site Tag and run the
 * full detector battery. Persisted signals dedupe within a 24h window and
 * auto-resolve when conditions clear — see runAndPersistSignals in
 * src/lib/services/signal-detection.ts.
 *
 * Auth: CRON_SECRET via Authorization: Bearer or ?secret= (same shape as
 * /api/cron/rank-check and /api/cron/refresh-tokens).
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { runAndPersistSignals } from "@/lib/services/signal-detection";
import { log } from "@/lib/logger";
import { logCron } from "@/lib/services/cron-logger";

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

const RUNTIME_CAP_MS = 240_000;
const CRON_NAME = "signal-detection";
const PAGE_SIZE = 200;

const CRON_SECRET = process.env.CRON_SECRET ?? "";

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

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

  return logCron("/api/cron/signal-detection", async () => {
  const startedAt = Date.now();

  const cursor = await prisma.cronCursor.findUnique({ where: { name: CRON_NAME } });
  const lastProcessedId = cursor?.brandId ?? null;

  // Only iterate brands that have a TrackedSite row. Detectors short-
  // circuit to empty results when the brand has no actual events yet, so
  // the cost of scanning an idle-tag brand is one cheap query.
  const brands = await prisma.brand.findMany({
    where: { trackedSite: { isNot: null }, isDemo: false },
    orderBy: { id: "asc" },
    take: PAGE_SIZE,
    ...(lastProcessedId ? { cursor: { id: lastProcessedId }, skip: 1 } : {}),
    select: { id: true, domain: true },
  });

  const results: Array<{
    brandId: string;
    domain: string;
    created: number;
    deduped: number;
    resolved: number;
    durationMs: number;
    skipped?: "runtime_cap";
    error?: string;
  }> = [];

  let lastProcessedBrandId: string | null = null;

  for (const brand of brands) {
    if (Date.now() - startedAt > RUNTIME_CAP_MS) {
      results.push({ brandId: brand.id, domain: brand.domain, created: 0, deduped: 0, resolved: 0, durationMs: 0, skipped: "runtime_cap" });
      continue;
    }
    try {
      const summary = await runAndPersistSignals(brand.id);
      results.push({
        brandId: brand.id,
        domain: brand.domain,
        created: summary.created,
        deduped: summary.deduped,
        resolved: summary.resolved,
        durationMs: summary.durationMs,
      });
    } catch (err) {
      const msg = err instanceof Error ? err.message : "unknown";
      results.push({ brandId: brand.id, domain: brand.domain, created: 0, deduped: 0, resolved: 0, durationMs: 0, error: msg });
      console.error(`[cron/signal-detection] ${brand.domain} failed: ${msg}`);
    }
    lastProcessedBrandId = brand.id;
  }

  const nextCursorId = brands.length >= PAGE_SIZE ? lastProcessedBrandId : null;
  await prisma.cronCursor.upsert({
    where: { name: CRON_NAME },
    create: { name: CRON_NAME, brandId: nextCursorId },
    update: { brandId: nextCursorId },
  });

  const totals = results.reduce(
    (acc, r) => ({
      created: acc.created + r.created,
      deduped: acc.deduped + r.deduped,
      resolved: acc.resolved + r.resolved,
    }),
    { created: 0, deduped: 0, resolved: 0 },
  );

  log.info("system", `[cron/signal-detection] ${brands.length} brands scanned · ${totals.created} new · ${totals.deduped} deduped · ${totals.resolved} resolved`, {
    metadata: { brands: brands.length, ...totals, durationMs: Date.now() - startedAt },
  });

  const payload = { brands: brands.length, ...totals, cursorReset: nextCursorId === null, results };
  return NextResponse.json(payload);
  });
}
