import { prisma } from "@/lib/prisma";

/** Audits older than this that are still status="running" get
 *  auto-marked as failed on the next read. Covers the Vercel-serverless
 *  case where the background runPerformanceAudit promise got killed
 *  mid-flight and the row was never updated.
 *
 *  Tightened from 10 minutes to 5 after the runner's wall-clock
 *  budget dropped to 55s: any "running" row older than the budget +
 *  a safety margin is definitely dead. Previously a 3-4 minute stale
 *  "running" row blocked the user from retrying without any visible
 *  explanation, because the route's concurrency guard treated it as
 *  a live in-progress audit. */
const STUCK_AUDIT_MINUTES = 5;

/**
 * Get the latest performance audit for a brand.
 * Optional sort: "performance" (default, lowest first) | "accessibility" | "seo" | "lcp"
 *
 * Side effect: recovers stuck audits. Any PerformanceAudit row still
 * flagged status="running" after STUCK_AUDIT_MINUTES (10 minutes)
 * gets auto-marked failed on read, so the UI can fall back to the
 * previous completed audit instead of spinning forever on a dead row.
 *
 * While status="running", this function returns progress info derived
 * from the live PerformancePageScore row count — no schema change
 * needed. Completed / failed audits return the full audit with
 * summary averages as before.
 */
export async function getLatestPerfAudit(
  brandId: string,
  device?: string,
  sort?: string
) {
  // Stuck-audit recovery — runs BEFORE the findFirst so a stuck row
  // doesn't keep masking the previous completed one.
  const staleCutoff = new Date(Date.now() - STUCK_AUDIT_MINUTES * 60 * 1000);
  await prisma.performanceAudit.updateMany({
    where: { brandId, status: "running", createdAt: { lt: staleCutoff } },
    data: { status: "failed", errorMessage: "Audit exceeded 10-minute runtime (process killed or crashed)", completedAt: new Date() },
  }).catch(() => { /* best-effort recovery */ });

  const audit = await prisma.performanceAudit.findFirst({
    where: { brandId, ...(device ? { device } : {}) },
    orderBy: { createdAt: "desc" },
    include: {
      pages: {
        orderBy: sort === "accessibility" ? { accessibility: "asc" }
               : sort === "seo" ? { seo: "asc" }
               : sort === "lcp" ? { lcpMs: "desc" }
               : { performance: "asc" },
      },
    },
  });

  if (!audit) return null;

  // Running audits surface progress — page count written so far —
  // with a sparse summary. Clients poll this endpoint and read
  // `progress` to render the "Testing page N of M" UI. The `pages`
  // array is whatever's been written so far; averages are computed
  // against those partial results.
  if (audit.status === "running") {
    // The previous completed audit (if any) so the UI can render
    // prior results alongside the in-flight progress indicator.
    const previous = await prisma.performanceAudit.findFirst({
      where: { brandId, ...(device ? { device } : {}), status: "completed" },
      orderBy: { createdAt: "desc" },
      include: { pages: true },
    }).catch(() => null);

    const partialCount = audit.pages.length;
    return {
      ...audit,
      progress: {
        status: "running" as const,
        // One URL ideally produces 2 rows (mobile + desktop), so
        // divide by 2 to approximate completed URL count. Caps at
        // 25 — the runner's MAX_PAGES ceiling.
        completedPages: Math.ceil(partialCount / 2),
        partialScores: partialCount,
        startedAt: audit.createdAt.toISOString(),
      },
      previous: previous
        ? {
            id: previous.id,
            completedAt: previous.completedAt?.toISOString() ?? null,
            pageCount: previous.pages.length,
          }
        : null,
      summary: null,
    };
  }

  // Calculate site-wide averages
  const pages = audit.pages;
  const count = pages.length || 1;

  const summary = {
    avgPerformance: Math.round(pages.reduce((s, p) => s + p.performance, 0) / count),
    avgAccessibility: Math.round(pages.reduce((s, p) => s + p.accessibility, 0) / count),
    avgBestPractices: Math.round(pages.reduce((s, p) => s + p.bestPractices, 0) / count),
    avgSeo: Math.round(pages.reduce((s, p) => s + p.seo, 0) / count),
    avgLcp: Math.round(pages.reduce((s, p) => s + p.lcpMs, 0) / count),
    avgInp: Math.round(pages.reduce((s, p) => s + p.inpMs, 0) / count),
    avgCls: Math.round(pages.reduce((s, p) => s + p.clsVal, 0) / count * 100) / 100,
    totalPages: count,
    passing: pages.filter((p) => p.status === "pass").length,
    failing: pages.filter((p) => p.status === "fail").length,
  };

  return { ...audit, summary };
}

/**
 * Compare two pages from the latest audit for a brand.
 * Returns side-by-side scores, differences, and a winner summary.
 */
export async function comparePages(
  brandId: string,
  pageUrlA: string,
  pageUrlB: string,
  device: string = "mobile"
) {
  const audit = await prisma.performanceAudit.findFirst({
    where: { brandId, device },
    orderBy: { createdAt: "desc" },
    include: { pages: true },
  });

  if (!audit) return null;

  const pageA = audit.pages.find((p) => p.pageUrl === pageUrlA);
  const pageB = audit.pages.find((p) => p.pageUrl === pageUrlB);

  if (!pageA || !pageB) return null;

  // Calculate differences (positive = A is better for scores, lower = better for timing)
  const metrics = [
    { label: "Performance",    key: "performance",   a: pageA.performance,   b: pageB.performance,   lowerBetter: false },
    { label: "Accessibility",  key: "accessibility", a: pageA.accessibility, b: pageB.accessibility, lowerBetter: false },
    { label: "Best Practices", key: "bestPractices", a: pageA.bestPractices, b: pageB.bestPractices, lowerBetter: false },
    { label: "SEO",            key: "seo",           a: pageA.seo,           b: pageB.seo,           lowerBetter: false },
    { label: "LCP",            key: "lcpMs",         a: pageA.lcpMs,         b: pageB.lcpMs,         lowerBetter: true, unit: "ms" },
    { label: "INP",            key: "inpMs",         a: pageA.inpMs,         b: pageB.inpMs,         lowerBetter: true, unit: "ms" },
    { label: "CLS",            key: "clsVal",        a: pageA.clsVal,        b: pageB.clsVal,        lowerBetter: true },
    { label: "FCP",            key: "fcpMs",         a: pageA.fcpMs,         b: pageB.fcpMs,         lowerBetter: true, unit: "ms" },
    { label: "TTFB",           key: "ttfbMs",        a: pageA.ttfbMs,        b: pageB.ttfbMs,        lowerBetter: true, unit: "ms" },
  ];

  const comparison = metrics.map((m) => {
    const diff = m.a - m.b;
    const aWins = m.lowerBetter ? m.a < m.b : m.a > m.b;
    const tie = m.a === m.b;
    return {
      ...m,
      diff,
      absDiff: Math.abs(diff),
      winner: tie ? "tie" as const : aWins ? "a" as const : "b" as const,
    };
  });

  // Overall winner (count wins)
  const aWins = comparison.filter((c) => c.winner === "a").length;
  const bWins = comparison.filter((c) => c.winner === "b").length;

  // Composite scores
  const compositeA = Math.round((pageA.performance + pageA.accessibility + pageA.bestPractices + pageA.seo) / 4);
  const compositeB = Math.round((pageB.performance + pageB.accessibility + pageB.bestPractices + pageB.seo) / 4);

  return {
    device,
    auditId: audit.id,
    auditDate: audit.createdAt,
    pageA: {
      url: pageA.pageUrl,
      status: pageA.status,
      composite: compositeA,
      scores: {
        performance: pageA.performance,
        accessibility: pageA.accessibility,
        bestPractices: pageA.bestPractices,
        seo: pageA.seo,
      },
      vitals: {
        lcpMs: pageA.lcpMs,
        inpMs: pageA.inpMs,
        clsVal: pageA.clsVal,
        fcpMs: pageA.fcpMs,
        ttfbMs: pageA.ttfbMs,
      },
    },
    pageB: {
      url: pageB.pageUrl,
      status: pageB.status,
      composite: compositeB,
      scores: {
        performance: pageB.performance,
        accessibility: pageB.accessibility,
        bestPractices: pageB.bestPractices,
        seo: pageB.seo,
      },
      vitals: {
        lcpMs: pageB.lcpMs,
        inpMs: pageB.inpMs,
        clsVal: pageB.clsVal,
        fcpMs: pageB.fcpMs,
        ttfbMs: pageB.ttfbMs,
      },
    },
    comparison,
    summary: {
      overallWinner: aWins > bWins ? "a" as const : bWins > aWins ? "b" as const : "tie" as const,
      aWins,
      bWins,
      ties: comparison.filter((c) => c.winner === "tie").length,
      compositeDiff: compositeA - compositeB,
    },
  };
}

/**
 * Create a performance audit with page-level scores.
 */
export async function createPerfAudit(brandId: string, device: string, pages: Array<{
  pageUrl: string;
  performance: number;
  accessibility: number;
  bestPractices: number;
  seo: number;
  lcpMs: number;
  inpMs: number;
  clsVal: number;
  fcpMs: number;
  ttfbMs: number;
}>) {
  return prisma.performanceAudit.create({
    data: {
      brandId,
      device,
      pages: {
        create: pages.map((p) => ({
          ...p,
          device,
          status: p.performance >= 90 ? "pass" : p.performance >= 50 ? "average" : "fail",
        })),
      },
    },
    include: { pages: true },
  });
}
