/**
 * POST /api/admin/backfill-detected-integrations
 *
 * Uses SQL aggregation (one query per brand) to populate DetectedIntegration
 * rows. Idempotent via upsert on brandId+name. Safe to run multiple times.
 *
 * Body: { brandId?: string }
 *   brandId present -> process single brand
 *   brandId omitted -> iterate all brands that have a TrackedSite
 *
 * Previous approach (JS-side aggregation with take:50000) hit the 30-second
 * statement_timeout when fetching 116K+ events for large brands. This version
 * delegates aggregation to Postgres -- one lightweight GROUP BY per brand.
 */

import { NextResponse } from "next/server";
import { z } from "zod";
import { prisma } from "@/lib/prisma";
import { requireSuperadmin } from "@/lib/admin-auth";
import { validateBody } from "@/lib/validation/request";
import { withErrorHandler } from "@/lib/api/with-error-handler";
import { BrandIdSchema } from "@/lib/validation/common-schemas";
import {
  lookupEnrichment,
  inferCategoryFromKey,
  type IntegrationCategory,
} from "@/lib/site-tag/integration-signatures";

export const dynamic = "force-dynamic";
// Iterating all brands sequentially: each brand query takes 1-3s.
// 25 brands * 3s = 75s worst-case; set ceiling well above that.
export const maxDuration = 300;

const BodySchema = z.object({
  brandId: BrandIdSchema.optional(),
});

interface AggregatedRow {
  plugin: string;
  plugin_key: string | null;
  category: string | null;
  first_seen: Date;
  last_seen: Date;
  event_count: bigint;
  page_count: bigint;
  sample_pages: string[] | null;
}

interface BrandSiteRow {
  brandId: string;
}

async function processOneBrand(
  brandId: string
): Promise<{ integrationsFound: number; upserted: number }> {
  const rows = await prisma.$queryRaw<AggregatedRow[]>`
    SELECT
      te.metadata->>'plugin'           AS plugin,
      te.metadata->>'plugin_key'       AS plugin_key,
      te.metadata->>'category'         AS category,
      MIN(te.timestamp)                AS first_seen,
      MAX(te.timestamp)                AS last_seen,
      COUNT(*)                         AS event_count,
      COUNT(DISTINCT te."pageUrl")     AS page_count,
      (array_agg(DISTINCT te."pageUrl"))[1:10] AS sample_pages
    FROM "TrackedEvent" te
    JOIN "TrackedSite" ts ON te."trackedSiteId" = ts.id
    WHERE ts."brandId"       = ${brandId}
      AND te."eventType"     = 'plugin_detected'
      AND te.metadata->>'plugin' IS NOT NULL
    GROUP BY
      te.metadata->>'plugin',
      te.metadata->>'plugin_key',
      te.metadata->>'category'
  `;

  let upserted = 0;

  for (const row of rows) {
    const plugin = row.plugin;
    const pluginKey = row.plugin_key ?? "";
    const enrichment = pluginKey ? lookupEnrichment(pluginKey) : null;
    const resolvedCategory = (
      row.category ||
      enrichment?.category ||
      (pluginKey ? inferCategoryFromKey(pluginKey) : "other")
    ) as IntegrationCategory;

    const pageCount = Number(row.page_count);
    const samplePages = Array.isArray(row.sample_pages)
      ? row.sample_pages.filter(Boolean)
      : [];

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    await (prisma as any).detectedIntegration.upsert({
      where: { brandId_name: { brandId, name: plugin } },
      create: {
        brandId,
        name: plugin,
        category: resolvedCategory,
        vendor: enrichment?.vendor ?? null,
        firstDetectedAt: row.first_seen,
        lastDetectedAt: row.last_seen,
        detectionSources: pluginKey ? [pluginKey] : [],
        pageCount,
        samplePages,
        evidence: { plugin_key: pluginKey, category: row.category },
        status: "active",
        isActive: true,
      },
      update: {
        lastDetectedAt: row.last_seen,
        pageCount,
        samplePages,
        vendor: enrichment?.vendor ?? null,
        status: "active",
        isActive: true,
      },
    });

    upserted++;
  }

  return { integrationsFound: rows.length, upserted };
}

export const POST = withErrorHandler(async (req: Request) => {
  const denied = await requireSuperadmin();
  if (denied) return denied;

  const { brandId } = await validateBody(req, BodySchema);

  const perBrand: Record<
    string,
    { integrationsFound: number; upserted: number; error?: string }
  > = {};
  let totalIntegrationsFound = 0;
  let totalUpserted = 0;

  if (brandId) {
    const stats = await processOneBrand(brandId);
    perBrand[brandId] = stats;
    totalIntegrationsFound = stats.integrationsFound;
    totalUpserted = stats.upserted;
  } else {
    // Discover all brands that have at least one TrackedSite, then
    // process each individually so no single query touches all brands.
    const sites = await prisma.$queryRaw<BrandSiteRow[]>`
      SELECT DISTINCT "brandId"
      FROM "TrackedSite"
      WHERE "brandId" IS NOT NULL
    `;

    for (const site of sites) {
      try {
        const stats = await processOneBrand(site.brandId);
        perBrand[site.brandId] = stats;
        totalIntegrationsFound += stats.integrationsFound;
        totalUpserted += stats.upserted;
      } catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        console.error(`[backfill] brand ${site.brandId} failed:`, msg);
        perBrand[site.brandId] = { integrationsFound: 0, upserted: 0, error: msg };
      }
    }
  }

  return NextResponse.json({
    mode: brandId ? "single-brand" : "all-brands",
    brandsProcessed: Object.keys(perBrand).length,
    integrationsFound: totalIntegrationsFound,
    upserted: totalUpserted,
    perBrand,
  });
});
