/**
 * GET /api/cron/platform-snapshot — monthly platform metrics archive.
 *
 * Runs on the 1st of each month (vercel.json). Computes the headline
 * platform metrics (DAU/WAU/MAU, totals across key tables, growth
 * rates, average brand health) and writes them to PlatformSnapshot
 * keyed by the snapshot date (YYYY-MM-01 UTC).
 *
 * The Admin Analytics + Data Export surfaces read from these
 * snapshots so month-over-month comparisons don't have to recompute
 * from raw events on every page load.
 *
 * Idempotent — re-running for the same month overwrites the row
 * (upsert on snapshotDate). Useful when adding a new metric mid-
 * month and back-filling the previous snapshot.
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { NOISE_SITE_EVENT_TYPES } from "@/lib/services/site-event-noise";

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

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;
  return new URL(req.url).searchParams.get("secret") === CRON_SECRET;
}

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

  // The snapshot represents the FIRST of the current month — running
  // on the 1st captures the prior month's totals at the boundary.
  const now = new Date();
  const snapshotDate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
  const monthStart = snapshotDate;
  const dayStart = new Date(now.getTime() - 86_400_000);
  const weekStart = new Date(now.getTime() - 7 * 86_400_000);

  // All counts run in parallel — each is a single COUNT() against an
  // indexed column so latency is dominated by the slowest one.
  const [
    totalUsers, totalBrands, totalOrgs,
    totalEvents, totalSessions, totalSiteEvents, totalConversions,
    totalGscQueries, totalGscPages, totalAiInteractions,
    siteTagsActive, dauUserIds, wauUserIds, mauUserIds,
    eventsLast30d, conversionsLast30d, brandsCreatedLast30d,
    auditAvg,
  ] = await Promise.all([
    prisma.user.count(),
    prisma.brand.count(),
    prisma.organization.count(),
    prisma.trackedEvent.count(),
    prisma.trackedSession.count(),
    // Exclude third_party_impact + slow_resource — these two types
    // are ~99% of SiteEvent volume and the cron hit the 30s budget
    // when the unfiltered full-table count ran on a hot connection
    // pool. The daily snapshot only needs to track behavioural
    // signals (rage/dead clicks, JS errors, outbound clicks), so
    // dropping the performance-observer rows keeps the trend
    // meaningful without sacrificing speed.
    prisma.siteEvent.count({
      where: { NOT: { eventType: { in: [...NOISE_SITE_EVENT_TYPES] } } },
    }),
    // intentionally unfiltered: cron job; counts all rows for system monitoring
    prisma.siteConversion.count(),
    prisma.gscQuery.count(),
    prisma.gscPage.count(),
    prisma.aiInteraction.count(),
    prisma.trackedSite.count({ where: { status: "active" } }),
    // DAU = distinct userId in PlatformEvent over the last 24h.
    prisma.platformEvent.findMany({
      where: { timestamp: { gte: dayStart }, userId: { not: null } },
      distinct: ["userId"],
      select: { userId: true },
    }),
    prisma.platformEvent.findMany({
      where: { timestamp: { gte: weekStart }, userId: { not: null } },
      distinct: ["userId"],
      select: { userId: true },
    }),
    prisma.platformEvent.findMany({
      where: { timestamp: { gte: monthStart }, userId: { not: null } },
      distinct: ["userId"],
      select: { userId: true },
    }),
    prisma.trackedEvent.count({ where: { timestamp: { gte: new Date(Date.now() - 30 * 86_400_000) } } }),
    // intentionally unfiltered: cron job; counts all rows for system monitoring
    prisma.siteConversion.count({ where: { timestamp: { gte: new Date(Date.now() - 30 * 86_400_000) } } }),
    prisma.brand.count({ where: { createdAt: { gte: new Date(Date.now() - 30 * 86_400_000) } } }),
    // Average crawlHealth across the latest audit per brand.
    prisma.technicalAudit.aggregate({
      _avg: { crawlHealth: true },
    }),
  ]);

  const dau = dauUserIds.length;
  const wau = wauUserIds.length;
  const mau = mauUserIds.length;
  const stickiness = mau > 0 ? Math.round((dau / mau) * 1000) / 10 : 0; // % with 1 decimal

  const metrics = {
    snapshotDate: snapshotDate.toISOString(),
    totals: {
      users: totalUsers,
      brands: totalBrands,
      organizations: totalOrgs,
      events: totalEvents,
      sessions: totalSessions,
      siteEvents: totalSiteEvents,
      conversions: totalConversions,
      gscQueries: totalGscQueries,
      gscPages: totalGscPages,
      aiInteractions: totalAiInteractions,
      siteTagsActive,
    },
    engagement: {
      dau, wau, mau,
      stickiness, // DAU/MAU * 100, % with 1 decimal
    },
    last30d: {
      events: eventsLast30d,
      conversions: conversionsLast30d,
      newBrands: brandsCreatedLast30d,
    },
    health: {
      averageBrandAuditScore: auditAvg._avg.crawlHealth ?? null,
    },
    runtimeMs: Date.now() - startedAt,
  };

  await prisma.platformSnapshot.upsert({
    where: { snapshotDate },
    create: { snapshotDate, metrics: metrics as object },
    update: { metrics: metrics as object },
  });

  // ── Per-brand daily MetricSnapshot for historical tracking ────
  // Captures Site Tag + AI metrics per brand per day so the
  // Historical Tracking page can show all-time cumulative totals
  // that survive data-retention purges. Fire-and-forget per brand
  // so one failing brand doesn't prevent the platform snapshot from
  // completing.
  try {
    const { getSessionCount, getPageViewCount, getConversionMetrics,
            getFormMetrics, getPhoneMetrics, getBookingMetrics,
            getEventCounts, getOutboundClickCount } = await import("@/lib/services/platform-metrics");
    const activeSites = await prisma.trackedSite.findMany({
      where: { status: "active", firstEventAt: { not: null } },
      select: { brandId: true },
      take: 50,
    });
    const todayStart = new Date(snapshotDate);
    todayStart.setHours(0, 0, 0, 0);
    for (const site of activeSites) {
      try {
        const [sess, pv, conv, forms, phone, bookings, events, outbound, ai] = await Promise.all([
          getSessionCount(site.brandId, todayStart),
          getPageViewCount(site.brandId, todayStart),
          getConversionMetrics(site.brandId, todayStart),
          getFormMetrics(site.brandId, todayStart),
          getPhoneMetrics(site.brandId, todayStart),
          getBookingMetrics(site.brandId, todayStart),
          getEventCounts(site.brandId, todayStart),
          getOutboundClickCount(site.brandId, todayStart),
          prisma.aiUsageLog.aggregate({
            where: { brandId: site.brandId, timestamp: { gte: todayStart } },
            _count: true,
            _sum: { estimatedCost: true },
          }).catch(() => ({ _count: 0, _sum: { estimatedCost: null } })),
        ]);
        await prisma.metricSnapshot.upsert({
          where: { brandId_date_source: { brandId: site.brandId, date: snapshotDate, source: "daily" } },
          create: {
            brandId: site.brandId, date: snapshotDate, source: "daily",
            siteTagSessions: sess, siteTagPageViews: pv,
            siteTagConversions: conv.total, siteTagFormSubmits: forms.submits,
            siteTagPhoneCalls: phone.total, siteTagBookings: bookings.confirmed,
            siteTagEvents: events.today, siteTagOutboundClicks: outbound,
            aiCalls: typeof ai._count === "number" ? ai._count : 0,
            aiCost: ai._sum.estimatedCost ?? 0,
          },
          update: {
            siteTagSessions: sess, siteTagPageViews: pv,
            siteTagConversions: conv.total, siteTagFormSubmits: forms.submits,
            siteTagPhoneCalls: phone.total, siteTagBookings: bookings.confirmed,
            siteTagEvents: events.today, siteTagOutboundClicks: outbound,
            aiCalls: typeof ai._count === "number" ? ai._count : 0,
            aiCost: ai._sum.estimatedCost ?? 0,
          },
        });
      } catch (err) {
        console.warn("[cron/platform-snapshot] brand metric snapshot failed", {
          brandId: site.brandId, err: err instanceof Error ? err.message : String(err),
        });
      }
    }
  } catch (err) {
    console.warn("[cron/platform-snapshot] daily metric snapshots failed", {
      err: err instanceof Error ? err.message : String(err),
    });
  }

  console.info("[cron/platform-snapshot]", { snapshotDate: snapshotDate.toISOString(), runtimeMs: metrics.runtimeMs });
  return NextResponse.json({ ok: true, snapshot: metrics });
}
