/**
 * GET /api/site-tag/analytics/rum?brandId=X&range=7d|30d|90d
 *
 * Backs the Real User Data tab on /site-performance-audit. Reads
 * RealUserMetric samples captured by t.js's web_vitals emitter and
 * returns:
 *
 *   {
 *     samples, windowDays,
 *     cwv: { lcp, fcp, cls, inp, ttfb },
 *     byConnection: [{ connection, samples, lcp, inp, cls }],
 *     byDevice:     [{ device,     samples, lcp, inp, cls }],
 *     topPages:     [{ url, samples, lcp, cls, inp, fcp, ttfb }],
 *   }
 *
 * Percentile choice: P75 for the headline + by-bucket views —
 * Google reports CWV as 75th-percentile values, and using P75
 * aligns this tab with the UX Insights CWV headline + the shared
 * getCoreWebVitals() helper in platform-metrics.ts. Outlier
 * ceilings (INP > 30s, LCP > 60s, CLS > 10) discard measurement
 * artefacts (bots, background tabs, frozen JS threads) before
 * aggregation. For the top-pages breakdown we also return the
 * sample count so the UI can mute rows with n < 5.
 *
 * Requires the caller to be a member of the brand's org — same
 * access model as the UX Insights route.
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";

export const dynamic = "force-dynamic";

const CACHE_HEADERS = {
  "Cache-Control": "private, max-age=30, stale-while-revalidate=120",
};

// Outlier ceilings — values above these are discarded before
// aggregation. Matches the ceilings in platform-metrics.ts
// getCoreWebVitals() so both surfaces agree on what counts as a
// valid sample. 10× Google's "poor" thresholds.
const CWV_CEIL = { lcp: 60_000, fcp: 60_000, cls: 10, inp: 30_000, ttfb: 60_000 };

type Metric = { lcp: number | null; fcp: number | null; cls: number | null; inp: number | null; ttfb: number | null };

function p75(values: number[]): number | null {
  const filtered = values.filter((v) => typeof v === "number" && Number.isFinite(v));
  if (filtered.length === 0) return null;
  filtered.sort((a, b) => a - b);
  return filtered[Math.floor(filtered.length * 0.75)];
}

function round(v: number | null, digits = 0): number | null {
  if (v == null || !Number.isFinite(v)) return null;
  const f = Math.pow(10, digits);
  return Math.round(v * f) / f;
}

function filterMetric(v: number | null | undefined, ceil: number): number {
  if (v == null || !Number.isFinite(v) || v >= ceil) return NaN;
  return v;
}

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

  const { searchParams } = new URL(req.url);
  const brandId = searchParams.get("brandId");
  const range = searchParams.get("range") || "30d";
  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 || !brand.organization.memberships.some((m) => m.userId === user.id)) {
    return NextResponse.json({ error: "Access denied" }, { status: 403 });
  }

  const days = range === "1d" ? 1 : range === "7d" ? 7 : range === "28d" ? 28 : range === "90d" ? 90 : 30;
  const since = new Date(Date.now() - days * 86_400_000);

  try {
    const rows = await prisma.realUserMetric.findMany({
      where: { brandId, timestamp: { gte: since } },
      select: {
        pageUrl: true,
        lcp: true, fcp: true, cls: true, inp: true, ttfb: true,
        device: true, connection: true,
      },
      orderBy: { timestamp: "desc" },
      // Cap at 10k — plenty for median aggregation even on a busy
      // site, and keeps the per-row JS math cheap.
      take: 10_000,
    });

    const samples = rows.length;

    // Headline P75 with outlier filtering — matches getCoreWebVitals
    // in platform-metrics.ts and the UX Insights tab's headline CWV.
    const cwv: Metric = {
      lcp:  round(p75(rows.map((r) => filterMetric(r.lcp, CWV_CEIL.lcp)))),
      fcp:  round(p75(rows.map((r) => filterMetric(r.fcp, CWV_CEIL.fcp)))),
      cls:  round(p75(rows.map((r) => filterMetric(r.cls, CWV_CEIL.cls))), 3),
      inp:  round(p75(rows.map((r) => filterMetric(r.inp, CWV_CEIL.inp)))),
      ttfb: round(p75(rows.map((r) => filterMetric(r.ttfb, CWV_CEIL.ttfb)))),
    };

    // Bucket by connection + device.
    const byConnectionMap: Record<string, typeof rows> = {};
    const byDeviceMap:     Record<string, typeof rows> = {};
    for (const r of rows) {
      const c = r.connection || "unknown";
      (byConnectionMap[c] ||= []).push(r);
      const d = r.device || "unknown";
      (byDeviceMap[d] ||= []).push(r);
    }
    const bucketStats = (rs: typeof rows) => ({
      samples: rs.length,
      lcp: round(p75(rs.map((r) => filterMetric(r.lcp, CWV_CEIL.lcp)))),
      inp: round(p75(rs.map((r) => filterMetric(r.inp, CWV_CEIL.inp)))),
      cls: round(p75(rs.map((r) => filterMetric(r.cls, CWV_CEIL.cls))), 3),
    });
    const byConnection = Object.entries(byConnectionMap)
      .map(([connection, rs]) => ({ connection, ...bucketStats(rs) }))
      .sort((a, b) => b.samples - a.samples);
    const byDevice = Object.entries(byDeviceMap)
      .map(([device, rs]) => ({ device, ...bucketStats(rs) }))
      .sort((a, b) => b.samples - a.samples);

    // Top pages by sample count with P75 + outlier filtering.
    const byPage: Record<string, typeof rows> = {};
    for (const r of rows) (byPage[r.pageUrl] ||= []).push(r);
    const topPages = Object.entries(byPage)
      .map(([url, rs]) => ({
        url,
        samples: rs.length,
        lcp:  round(p75(rs.map((r) => filterMetric(r.lcp, CWV_CEIL.lcp)))),
        fcp:  round(p75(rs.map((r) => filterMetric(r.fcp, CWV_CEIL.fcp)))),
        cls:  round(p75(rs.map((r) => filterMetric(r.cls, CWV_CEIL.cls))), 3),
        inp:  round(p75(rs.map((r) => filterMetric(r.inp, CWV_CEIL.inp)))),
        ttfb: round(p75(rs.map((r) => filterMetric(r.ttfb, CWV_CEIL.ttfb)))),
      }))
      .sort((a, b) => b.samples - a.samples)
      .slice(0, 25);

    return NextResponse.json(
      { samples, windowDays: days, cwv, byConnection, byDevice, topPages },
      { headers: CACHE_HEADERS },
    );
  } catch (err) {
    console.error("[site-tag/analytics:rum] failed", err);
    // Return an empty-shape payload so the UI can still render.
    return NextResponse.json({
      samples: 0,
      windowDays: days,
      cwv: { lcp: null, fcp: null, cls: null, inp: null, ttfb: null },
      byConnection: [],
      byDevice: [],
      topPages: [],
    });
  }
}
