/**
 * GET /api/cron/precompute-dashboard
 *
 * Runs every 30 minutes. For each active brand, fetches the main
 * /api/dashboard endpoint internally and upserts the result into
 * DashboardSnapshot. This ensures the /api/dashboard/summary endpoint
 * always has a recent payload to serve instantly.
 *
 * Schedule: every 30 minutes
 */

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

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

export async function GET(req: Request) {
  const cronSecret = process.env.CRON_SECRET;
  if (!cronSecret) {
    return NextResponse.json({ error: "Server misconfigured: CRON_SECRET unset" }, { status: 500 });
  }
  const authHeader = req.headers.get("authorization");
  const querySecret = new URL(req.url).searchParams.get("secret");
  if (authHeader !== `Bearer ${cronSecret}` && querySecret !== cronSecret) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return logCron("/api/cron/precompute-dashboard", async () => {
    const brands = await prisma.brand.findMany({
      where: { status: { not: "deleted" }, isDemo: false },
      select: { id: true, name: true },
    });

    const ranges = ["28d"];
    const results: Array<{ brand: string; range: string; status: string; ms: number }> = [];
    const origin = new URL(req.url).origin;

    // Per-brand timeout: abort the fetch if a single brand takes longer than
    // this. Brands that exceed it are logged as "skipped_timeout" and the loop
    // continues so one slow brand cannot starve the rest.
    const PER_BRAND_BUDGET_MS = 25_000;

    // Function-level budget: maxDuration is 120s. Stop starting new brands
    // when less than one full per-brand budget remains, so the function exits
    // cleanly rather than being killed mid-flight by Vercel.
    const FUNCTION_BUDGET_MS = 110_000;
    const cronStart = Date.now();

    for (const brand of brands) {
      // Guard: if not enough function budget remains for even one more brand,
      // record remaining brands as skipped and break.
      if (Date.now() - cronStart + PER_BRAND_BUDGET_MS > FUNCTION_BUDGET_MS) {
        console.warn(`[precompute-dashboard] function budget exhausted, skipping brand ${brand.name} and remaining`);
        results.push({ brand: brand.name, range: "28d", status: "skipped_budget", ms: 0 });
        continue;
      }

      for (const range of ranges) {
        const start = Date.now();
        try {
          const res = await fetch(
            `${origin}/api/dashboard?brandId=${brand.id}&range=${range}`,
            {
              headers: cronSecret ? { authorization: `Bearer ${cronSecret}` } : {},
              signal: AbortSignal.timeout(PER_BRAND_BUDGET_MS),
            },
          );

          if (!res.ok) {
            results.push({ brand: brand.name, range, status: `http_${res.status}`, ms: Date.now() - start });
            continue;
          }

          const data = await res.json();

          await prisma.dashboardSnapshot.upsert({
            where: { brandId_range: { brandId: brand.id, range } },
            create: {
              brandId: brand.id,
              range,
              payload: JSON.parse(JSON.stringify(data)),
              computedAt: new Date(),
            },
            update: {
              payload: JSON.parse(JSON.stringify(data)),
              computedAt: new Date(),
            },
          });

          results.push({ brand: brand.name, range, status: "ok", ms: Date.now() - start });
        } catch (err) {
          const elapsed = Date.now() - start;
          const isTimeout = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
          if (isTimeout) {
            console.warn(`[precompute-dashboard] brand ${brand.name} exceeded ${PER_BRAND_BUDGET_MS}ms budget, skipping`);
            results.push({ brand: brand.name, range, status: "skipped_timeout", ms: elapsed });
          } else {
            results.push({
              brand: brand.name,
              range,
              status: err instanceof Error ? err.message.slice(0, 80) : "error",
              ms: elapsed,
            });
          }
        }
      }
    }

    const payload = {
      brands: brands.length,
      computed: results.filter((r) => r.status === "ok").length,
      skipped: results.filter((r) => r.status.startsWith("skipped_")).length,
      failed: results.filter((r) => r.status !== "ok" && !r.status.startsWith("skipped_")).length,
      results,
    };
    return NextResponse.json(payload);
  });
}
