/**
 * GET /api/admin/historical?brandId=all|<id>&metric=all|gsc|ga4|site_tag
 *
 * Returns all-time cumulative metrics, per-brand breakdown, monthly
 * time series, and data-retention status. Reads from MetricSnapshot
 * (permanent storage) + live tables for the current window.
 */
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireSuperadmin, withAdminTiming } from "@/lib/admin-auth";

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

export async function GET(req: Request) {
  const denied = await requireSuperadmin();
  if (denied) return denied;

  const { searchParams } = new URL(req.url);
  const brandIdParam = searchParams.get("brandId") ?? "all";
  const brandFilter = brandIdParam === "all" ? {} : { brandId: brandIdParam };

  return NextResponse.json(await withAdminTiming("historical", async () => {
    console.info("[historical] brandFilter:", brandIdParam, brandFilter);

    // ── GSC totals — ALWAYS from GscPage (the raw archive) ──────
    // MetricSnapshot.organicClicks can be mis-synced (wrong brandId
    // on snapshot rows from early sync code). GscPage rows are
    // written per-page-per-day by the GSC backfill with the correct
    // brandId — they're the authoritative source for GSC numbers.
    const bw = (brandFilter.brandId ? { brandId: brandFilter.brandId as string } : {}) as { brandId?: string };
    const gscRaw = await prisma.gscPage.aggregate({
      where: bw,
      _sum: { clicks: true, impressions: true },
      _avg: { position: true, ctr: true },
    }).catch(() => ({ _sum: { clicks: null, impressions: null }, _avg: { position: null, ctr: null } }));

    // ── GA4 totals — from MetricSnapshot, only for brands with GA4 connected ──
    // Without this gate, orphaned MetricSnapshot rows from brands that
    // disconnected GA4 (or never had it) pollute the totals.
    const ga4ConnectedBrandIds = await prisma.brandIntegration.findMany({
      where: { integrationId: "ga4", connected: true, ...(brandFilter.brandId ? { brandId: brandFilter.brandId as string } : {}) },
      select: { brandId: true },
    }).then((rows) => rows.map((r) => r.brandId)).catch(() => [] as string[]);
    const ga4TotalsWhere = ga4ConnectedBrandIds.length > 0
      ? { brandId: { in: ga4ConnectedBrandIds }, source: "ga4" as const }
      : { brandId: "___none___", source: "ga4" as const };
    const ga4Agg = await prisma.metricSnapshot.aggregate({
      where: ga4TotalsWhere,
      _sum: { sessions: true, users: true, conversions: true, revenue: true },
    }).catch(() => ({ _sum: { sessions: null, users: null, conversions: null, revenue: null } }));

    // ── Site Tag totals — from raw tables (always authoritative) ──
    const [rawSessions, rawPageViews, rawSiteConv, rawEvents, rawFormSubmits, rawPhoneCalls, rawBookings, rawOutbound] = await Promise.all([
      prisma.trackedSession.count({ where: bw.brandId ? { trackedSite: { brandId: bw.brandId } } : {} }).catch(() => 0),
      prisma.trackedEvent.count({ where: { ...(bw.brandId ? { trackedSite: { brandId: bw.brandId } } : {}), eventType: "page_view" } }).catch(() => 0),
      // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
      prisma.siteConversion.count({ where: bw }).catch(() => 0),
      prisma.trackedEvent.count({ where: bw.brandId ? { trackedSite: { brandId: bw.brandId } } : {} }).catch(() => 0),
      // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
      prisma.siteConversion.count({ where: { ...bw, conversionType: { in: ["form_submit", "form_submitted", "form_submission"] } } }).catch(() => 0),
      // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
      prisma.siteConversion.count({ where: { ...bw, conversionType: { in: ["phone_number_click", "phone_call", "click_to_call"] } } }).catch(() => 0),
      // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
      prisma.siteConversion.count({ where: { ...bw, conversionType: { in: ["appointment_booked", "booking_confirmed"] } } }).catch(() => 0),
      prisma.trackedEvent.count({ where: { ...(bw.brandId ? { trackedSite: { brandId: bw.brandId } } : {}), eventType: "outbound_click" } }).catch(() => 0),
    ]);

    // ── AI / Paid ──
    // AI totals: read from AiInteraction directly (the raw table).
    // MetricSnapshot.aiCalls only has data from the daily cron which
    // hasn't accumulated history yet — reading the raw table shows
    // the real 292+ calls and $9.40 cost immediately.
    // AiInteraction.brandId is nullable — some system-level calls
    // don't carry it. When a specific brand is selected, scope to
    // that brand; when "all", count everything.
    const [aiAgg, paidAgg] = await Promise.all([
      prisma.aiInteraction.aggregate({
        where: bw.brandId ? { brandId: bw.brandId } : {},
        _count: true,
        _sum: { costEstimate: true },
      }).catch(() => ({ _count: 0 as number, _sum: { costEstimate: null as number | null } })),
      prisma.metricSnapshot.aggregate({
        where: brandFilter,
        _sum: { adSpend: true, adClicks: true, adImpressions: true, adConversions: true },
      }).catch(() => ({ _sum: {} as Record<string, null> })),
    ]);
    const pa = paidAgg._sum as Record<string, number | null>;

    const totals = {
      gsc: {
        totalClicks: gscRaw._sum.clicks ?? 0,
        totalImpressions: gscRaw._sum.impressions ?? 0,
        avgPosition: Math.round((gscRaw._avg.position ?? 0) * 10) / 10,
        avgCtr: Math.round((gscRaw._avg.ctr ?? 0) * 10000) / 100,
      },
      ga4: {
        totalSessions: ga4Agg._sum.sessions ?? 0,
        totalUsers: ga4Agg._sum.users ?? 0,
        totalKeyEvents: ga4Agg._sum.conversions ?? 0,
        totalRevenue: ga4Agg._sum.revenue ?? 0,
        note: "GA4 'key events' are event-level counts, not unique converting sessions. A single session can fire multiple key events.",
      },
      siteTag: {
        totalSessions: rawSessions,
        totalPageViews: rawPageViews,
        totalConversions: rawSiteConv,
        totalFormSubmits: rawFormSubmits,
        totalPhoneCalls: rawPhoneCalls,
        totalBookings: rawBookings,
        totalEvents: rawEvents,
        totalOutboundClicks: rawOutbound,
      },
      platform: {
        totalAiCalls: typeof aiAgg._count === "number" ? aiAgg._count : 0,
        totalAiCost: Math.round((aiAgg._sum.costEstimate ?? 0) * 100) / 100,
      },
      paid: {
        totalSpend: Math.round((pa.adSpend ?? 0) * 100) / 100,
        totalAdClicks: pa.adClicks ?? 0,
        totalAdImpressions: pa.adImpressions ?? 0,
        totalAdConversions: pa.adConversions ?? 0,
      },
    };

    // Per-brand breakdown — GSC from GscPage (authoritative), GA4
    // from MetricSnapshot source='ga4'. This prevents MetricSnapshot
    // rows with wrong brandId from showing incorrect GSC numbers
    // (the Growth360Partners 14.3K bug).
    const [gscByBrand, ga4ByBrand] = await Promise.all([
      prisma.gscPage.groupBy({
        by: ["brandId"],
        ...(brandFilter.brandId ? { where: { brandId: brandFilter.brandId as string } } : {}),
        _sum: { clicks: true, impressions: true },
      }).catch(() => [] as Array<{ brandId: string; _sum: { clicks: number | null; impressions: number | null } }>),
      prisma.metricSnapshot.groupBy({
        by: ["brandId"],
        where: { ...brandFilter, source: "ga4" },
        _sum: { sessions: true, conversions: true },
        _min: { date: true },
        _max: { date: true },
        _count: true,
      }).catch(() => [] as Array<{ brandId: string; _sum: { sessions: number | null; conversions: number | null }; _min: { date: Date | null }; _max: { date: Date | null }; _count: number }>),
    ]);

    // Authoritative brand list — every brand the admin should see,
    // regardless of whether they have any telemetry data yet.
    // When a specific brand is selected, scope to just that one.
    const allBrands = await prisma.brand.findMany({
      where: brandFilter.brandId ? { id: brandFilter.brandId as string } : {},
      select: { id: true, name: true, domain: true, status: true, statusChangedAt: true, statusChangedBy: true, snippetDetectedAt: true, snippetInstallationMethod: true, snippetCheckedAt: true },
      orderBy: { name: "asc" },
    });
    const allBrandIds = allBrands.map((b) => b.id);

    // Check which brands actually have GA4 / GSC connected — prevents
    // showing stale MetricSnapshot data for brands whose integration
    // was disconnected or never existed (Growth360Partners bug).
    const connectedIntegrations = await prisma.brandIntegration.findMany({
      where: {
        brandId: { in: allBrandIds },
        integrationId: { in: ["ga4", "gsc"] },
        connected: true,
      },
      select: { brandId: true, integrationId: true },
    }).catch(() => [] as Array<{ brandId: string; integrationId: string }>);
    const ga4Connected = new Set(connectedIntegrations.filter((i) => i.integrationId === "ga4").map((i) => i.brandId));
    const gscConnected = new Set(connectedIntegrations.filter((i) => i.integrationId === "gsc").map((i) => i.brandId));

    const gscMap = new Map(gscByBrand.map((r) => [r.brandId, r._sum]));
    const ga4Map = new Map(ga4ByBrand.map((r) => [r.brandId, { sum: r._sum, min: r._min, max: r._max, count: r._count }]));

    const brands = allBrands.map((brand) => {
      const bid = brand.id;
      const gsc = gscConnected.has(bid) ? gscMap.get(bid) : null;
      const ga4 = ga4Connected.has(bid) ? ga4Map.get(bid) : null;
      return {
        brandId: bid,
        name: brand.name,
        domain: brand.domain,
        status: brand.status,
        statusChangedAt: brand.statusChangedAt?.toISOString() ?? null,
        statusChangedBy: brand.statusChangedBy ?? null,
        snippetDetectedAt: brand.snippetDetectedAt?.toISOString() ?? null,
        snippetInstallationMethod: brand.snippetInstallationMethod ?? null,
        snippetCheckedAt: brand.snippetCheckedAt?.toISOString() ?? null,
        gscClicks: gsc?.clicks ?? 0,
        gscImpressions: gsc?.impressions ?? 0,
        ga4Sessions: ga4?.sum.sessions ?? 0,
        ga4KeyEvents: ga4?.sum.conversions ?? 0,
        ga4Connected: ga4Connected.has(bid),
        gscConnected: gscConnected.has(bid),
        siteTagSessions: 0, siteTagConversions: 0, siteTagEvents: 0,
        firstDataDate: ga4?.min.date?.toISOString().split("T")[0] ?? null,
        lastDataDate: ga4?.max.date?.toISOString().split("T")[0] ?? null,
        snapshots: ga4?.count ?? 0,
      };
    }).sort((a, b) => (b.gscClicks + b.ga4Sessions) - (a.gscClicks + a.ga4Sessions));

    // Monthly time series — merge TWO sources:
    //   1. GscPage (raw archive) → GSC clicks + impressions per month
    //   2. MetricSnapshot → GA4 sessions/conversions + Site Tag metrics
    // GscPage is authoritative for GSC; MetricSnapshot can have
    // mis-synced brandId issues so we never read GSC from it.
    const [gscMonthly, snapRows] = await Promise.all([
      prisma.gscPage.groupBy({
        by: ["date"],
        where: bw,
        _sum: { clicks: true, impressions: true },
      }).catch(() => [] as Array<{ date: Date; _sum: { clicks: number | null; impressions: number | null } }>),
      prisma.metricSnapshot.findMany({
        where: { ...brandFilter, source: "ga4" },
        select: { date: true, sessions: true, conversions: true, siteTagSessions: true, siteTagConversions: true, siteTagEvents: true },
        orderBy: { date: "asc" },
        take: 50_000,
      }).catch(() => []),
    ]);

    const monthlyMap = new Map<string, {
      gscClicks: number; gscImpressions: number;
      ga4Sessions: number; ga4Conversions: number;
      siteTagSessions: number; siteTagConversions: number; siteTagEvents: number;
    }>();
    const emptyMonth = () => ({ gscClicks: 0, gscImpressions: 0, ga4Sessions: 0, ga4Conversions: 0, siteTagSessions: 0, siteTagConversions: 0, siteTagEvents: 0 });

    // GSC from raw GscPage
    for (const r of gscMonthly) {
      const m = r.date.toISOString().slice(0, 7);
      const e = monthlyMap.get(m) ?? emptyMonth();
      e.gscClicks += r._sum.clicks ?? 0;
      e.gscImpressions += r._sum.impressions ?? 0;
      monthlyMap.set(m, e);
    }
    // GA4 + Site Tag from MetricSnapshot (source=ga4 only)
    for (const r of snapRows) {
      const m = r.date.toISOString().slice(0, 7);
      const e = monthlyMap.get(m) ?? emptyMonth();
      e.ga4Sessions += r.sessions;
      e.ga4Conversions += r.conversions;
      e.siteTagSessions += r.siteTagSessions ?? 0;
      e.siteTagConversions += r.siteTagConversions ?? 0;
      e.siteTagEvents += r.siteTagEvents ?? 0;
      monthlyMap.set(m, e);
    }
    const monthly = [...monthlyMap.entries()]
      .map(([month, d]) => ({ month, ...d }))
      .sort((a, b) => a.month.localeCompare(b.month));

    // (Raw-table queries for Site Tag totals are now done inline
    // above — the old "merge raw totals" block is no longer needed
    // because totals are computed directly from GscPage + raw tables
    // instead of from MetricSnapshot.)

    // Connection status — when a specific brand is selected, surface
    // which integrations are actually connected so the UI can flag
    // "GA4 not connected — numbers may be from a historical sync"
    // instead of showing potentially misleading totals.
    let connections: { gsc: boolean; ga4: boolean; siteTag: boolean } | null = null;
    if (brandFilter.brandId) {
      const [gscConn, ga4Conn, stConn] = await Promise.all([
        prisma.brandIntegration.findFirst({ where: { brandId: brandFilter.brandId as string, integrationId: "gsc", connected: true }, select: { id: true } }).catch(() => null),
        prisma.brandIntegration.findFirst({ where: { brandId: brandFilter.brandId as string, integrationId: "ga4", connected: true }, select: { id: true } }).catch(() => null),
        prisma.trackedSite.findFirst({ where: { brandId: brandFilter.brandId as string, firstEventAt: { not: null } }, select: { id: true } }).catch(() => null),
      ]);
      connections = { gsc: !!gscConn, ga4: !!ga4Conn, siteTag: !!stConn };
    }

    // Data retention info — scope to selected brand when filtered
    const [gscOldest, siteTagOldest, snapshotOldest, totalBrands, totalSnapshots] = await Promise.all([
      prisma.gscPage.findFirst({ where: brandFilter, orderBy: { date: "asc" }, select: { date: true } }).catch(() => null),
      prisma.trackedSite.findFirst({ where: { ...(bw.brandId ? { brandId: bw.brandId } : {}), firstEventAt: { not: null } }, orderBy: { firstEventAt: "asc" }, select: { firstEventAt: true } }).catch(() => null),
      prisma.metricSnapshot.findFirst({ where: brandFilter, orderBy: { date: "asc" }, select: { date: true } }).catch(() => null),
      prisma.brand.count().catch(() => 0),
      prisma.metricSnapshot.count({ where: brandFilter }).catch(() => 0),
    ]);

    const firstDate = gscOldest?.date ?? snapshotOldest?.date ?? new Date();
    const daySpan = Math.max(1, Math.round((Date.now() - firstDate.getTime()) / 86_400_000));

    return {
      totals,
      brands,
      monthly,
      connections,
      selectedBrandId: brandFilter.brandId ?? "all",
      retention: {
        gscOldestData: gscOldest?.date?.toISOString().split("T")[0] ?? null,
        gscRetentionMonths: 16,
        siteTagOldestData: siteTagOldest?.firstEventAt?.toISOString().split("T")[0] ?? null,
        snapshotsOldestData: snapshotOldest?.date?.toISOString().split("T")[0] ?? null,
        note: "Snapshots preserve data permanently even after GSC/GA4 retention windows expire.",
      },
      valuation: {
        totalBrands,
        totalSnapshots,
        dataSpanDays: daySpan,
        rawArchive: {
          gscPageRows: gscRaw._sum.clicks != null ? "present" : "empty",
          siteConversionRows: rawSiteConv,
          trackedSessionRows: rawSessions,
          trackedEventRows: rawEvents,
        },
      },
    };
  }), {
    headers: { "Cache-Control": "private, max-age=300, stale-while-revalidate=600" },
  });
}
