/**
 * POST /api/performance-audit/run
 * Body: { brandId }
 * Triggers a bounded Core Web Vitals audit for a brand.
 *
 * Wall-clock budget: the runner self-caps at 55s (MAX_PAGES=5,
 * MAX_CONCURRENCY=3, per-call timeout 30s). Vercel maxDuration is
 * 60s — 5s of headroom so the route can still emit its JSON response
 * after the runner closes out. Anything older than that must have
 * been killed mid-flight and is recovered by getLatestPerfAudit's
 * stuck-audit sweep.
 *
 * Concurrency guard: if a "running" audit already exists for the
 * brand and started in the last STALE_RUN_MINUTES, return that
 * audit's id instead of starting a duplicate. Prevents accidental
 * double-fire from impatient users hitting the button twice. If the
 * existing row is older than STALE_RUN_MINUTES, auto-mark it failed
 * in place so the user's click triggers a fresh run instead of
 * returning an id that will never resolve.
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { runPerformanceAudit } from "@/lib/services/performance-audit-runner";

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

// Short enough to bound a "phantom running" row's damage, long enough
// to cover the 55s runner budget + DB latency. Matches
// STUCK_AUDIT_MINUTES in performance-audit.ts for symmetry.
const STALE_RUN_MINUTES = 5;

export async function POST(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { brandId } = await req.json();
  if (!brandId) return NextResponse.json({ error: "brandId required" }, { status: 400 });

  const brand = await prisma.brand.findUnique({
    where: { id: brandId },
    include: { organization: { include: { memberships: true } } },
  });
  if (!brand) return NextResponse.json({ error: "Brand not found" }, { status: 404 });
  if (!brand.organization.memberships.some((m) => m.userId === user.id)) {
    return NextResponse.json({ error: "Access denied" }, { status: 403 });
  }

  // Unstick any "running" rows older than the stale cutoff BEFORE
  // the concurrency guard. Without this step, a row left behind by
  // a crashed Vercel invocation would masquerade as "in progress"
  // and block every subsequent retry for the full staleCutoff
  // window — the user's Run button would just return the dead
  // auditId over and over. Explicit sweep here (duplicated in
  // getLatestPerfAudit for the read path) means both enqueue + read
  // recover independently.
  const staleCutoff = new Date(Date.now() - STALE_RUN_MINUTES * 60 * 1000);
  await prisma.performanceAudit.updateMany({
    where: { brandId, status: "running", createdAt: { lt: staleCutoff } },
    data: {
      status: "failed",
      errorMessage: `Audit exceeded ${STALE_RUN_MINUTES}-minute runtime (process killed or crashed before completion).`,
      completedAt: new Date(),
    },
  }).catch(() => { /* best-effort — don't block a new run on sweep failure */ });

  // Concurrency guard. A "running" row that started within the stale
  // cutoff wins — return its id with a status note so the UI can
  // surface "Already running" instead of double-firing a crawl.
  const running = await prisma.performanceAudit.findFirst({
    where: { brandId, status: "running", createdAt: { gte: staleCutoff } },
    orderBy: { createdAt: "desc" },
    select: { id: true, createdAt: true },
  });
  if (running) {
    return NextResponse.json({
      auditId: running.id,
      status: "already_running",
      message: `Audit already in progress (started ${Math.round((Date.now() - running.createdAt.getTime()) / 1000)}s ago).`,
    });
  }

  const result = await runPerformanceAudit(brandId, brand.domain);
  return NextResponse.json(result);
}
