/**
 * GET /api/admin/debug/conversion-rows
 *
 * Read-only diagnostic with two modes (select via ?mode=):
 *
 * mode=taxonomy  (default when no mode given)
 *   No filters except brandId + last 30 days. Returns:
 *     - total row count with NO other filter
 *     - earliest / latest timestamp
 *     - counts grouped by conversionType
 *     - counts grouped by formProvider
 *     - counts grouped by providerCategory
 *   Use this first to confirm data exists and see the exact stored values
 *   before applying any category filter.
 *
 * mode=rows
 *   Returns the exact rows counted by Embedded Conversions "Iframe Form".
 *   Uses the SAME WHERE as embedded-conversions baseWhere:
 *     brandId, timestamp window, isDuplicateOf IS NULL,
 *     invalidatedReason IS NULL, formProvider IS NOT NULL,
 *     AND (providerCategory = 'form'
 *          OR (providerCategory IS NULL AND formProvider IN <form-providers>))
 *   sessionId / visitorId are HMAC-SHA256 hashed; tracking params stripped.
 *
 * Query params:
 *   brandId       (required)
 *   mode          (taxonomy | rows, default taxonomy)
 *   date          (YYYY-MM-DD, default 2026-06-04 -- rows mode only)
 *   include_dupes (0|1, default 0 -- rows mode only)
 *
 * Superadmin-only, read-only, no writes.
 */

import { NextResponse } from "next/server";
import crypto from "crypto";
import { prisma } from "@/lib/prisma";
import { requireSuperadmin, withAdminTiming } from "@/lib/admin-auth";

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

// Providers whose rows map to category "form" (Iframe Form) in embedded-
// conversions when providerCategory is null (rows written before the column
// was added). Kept in sync with PROVIDER_TO_CATEGORY in embedded-conversions/route.ts.
const FORM_PROVIDERS = [
  "hubspot", "typeform", "jotform", "formstack", "wufoo", "gohighlevel", "native",
];

// Tracking query-param names to strip before returning pageUrl.
const TRACKING_PARAMS = new Set([
  "gclid", "fbclid", "msclkid", "ttclid", "twclid", "li_fat_id",
  "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content",
  "utm_id", "utm_source_platform", "utm_creative_format", "utm_marketing_tactic",
  "_ga", "_gl", "gbraid", "wbraid", "dclid",
]);

function stripTrackingParams(rawUrl: string): string {
  try {
    const u = new URL(rawUrl);
    for (const key of [...u.searchParams.keys()]) {
      if (TRACKING_PARAMS.has(key) || key.startsWith("utm_")) {
        u.searchParams.delete(key);
      }
    }
    return u.toString();
  } catch {
    return rawUrl;
  }
}

// One-way hash for a session/visitor id. The salt is unique per request
// so the output is not reversible across separate curl calls, but rows
// within a single response that share a session will still share the
// same hash value.
function makeHasher(salt: string) {
  return (id: string | null): string => {
    if (!id) return "null";
    return crypto
      .createHmac("sha256", salt)
      .update(id)
      .digest("hex")
      .slice(0, 16);
  };
}

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

  const { searchParams } = new URL(req.url);
  const brandId = searchParams.get("brandId");
  const mode = searchParams.get("mode") ?? "taxonomy";

  if (!brandId) {
    return NextResponse.json(
      {
        error: "brandId required.",
        usage: "?brandId=<id>&mode=taxonomy  OR  ?brandId=<id>&mode=rows[&date=YYYY-MM-DD][&include_dupes=1]",
      },
      { status: 400 },
    );
  }

  if (mode !== "taxonomy" && mode !== "rows") {
    return NextResponse.json(
      { error: "mode must be 'taxonomy' or 'rows'" },
      { status: 400 },
    );
  }

  // ── taxonomy mode ──────────────────────────────────────────────────────
  if (mode === "taxonomy") {
    const since = new Date(Date.now() - 30 * 86_400_000);

    return NextResponse.json(
      await withAdminTiming("conversion-rows-taxonomy", async () => {
        const [
          totalAll,
          timestampBounds,
          byConversionType,
          byFormProvider,
          byProviderCategory,
        ] = await Promise.all([
          // Total rows with NO filter beyond brandId + 30-day window
          // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
          prisma.siteConversion.count({
            where: { brandId, timestamp: { gte: since } },
          }),
          // Earliest + latest timestamp present
          // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
          prisma.siteConversion.aggregate({
            where: { brandId, timestamp: { gte: since } },
            _min: { timestamp: true },
            _max: { timestamp: true },
          }),
          // Grouped by conversionType
          // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
          prisma.siteConversion.groupBy({
            by: ["conversionType"],
            where: { brandId, timestamp: { gte: since } },
            _count: { _all: true },
            orderBy: { _count: { conversionType: "desc" } },
          }),
          // Grouped by formProvider (null = no provider set)
          // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
          prisma.siteConversion.groupBy({
            by: ["formProvider"],
            where: { brandId, timestamp: { gte: since } },
            _count: { _all: true },
            orderBy: { _count: { formProvider: "desc" } },
          }),
          // Grouped by providerCategory (null = column not set)
          // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
          prisma.siteConversion.groupBy({
            by: ["providerCategory"],
            where: { brandId, timestamp: { gte: since } },
            _count: { _all: true },
            orderBy: { _count: { providerCategory: "desc" } },
          }),
        ]);

        return {
          mode: "taxonomy",
          brandId,
          window: { since: since.toISOString(), days: 30 },
          totalRows: totalAll,
          earliestTimestamp: timestampBounds._min.timestamp?.toISOString() ?? null,
          latestTimestamp:   timestampBounds._max.timestamp?.toISOString() ?? null,
          byConversionType: byConversionType.map((r) => ({
            conversionType: r.conversionType,
            count: r._count._all,
          })),
          byFormProvider: byFormProvider.map((r) => ({
            formProvider: r.formProvider ?? "(null)",
            count: r._count._all,
          })),
          byProviderCategory: byProviderCategory.map((r) => ({
            providerCategory: r.providerCategory ?? "(null)",
            count: r._count._all,
          })),
        };
      }),
    );
  }

  // ── rows mode ──────────────────────────────────────────────────────────
  const dateParam = searchParams.get("date") ?? "2026-06-04";
  const includeDupes = searchParams.get("include_dupes") === "1";

  if (!/^\d{4}-\d{2}-\d{2}$/.test(dateParam)) {
    return NextResponse.json({ error: "date must be YYYY-MM-DD" }, { status: 400 });
  }

  const dayStart = new Date(`${dateParam}T00:00:00.000Z`);
  const dayEnd   = new Date(`${dateParam}T23:59:59.999Z`);

  return NextResponse.json(
    await withAdminTiming("conversion-rows", async () => {
      const salt = crypto.randomBytes(16).toString("hex");
      const hash = makeHasher(salt);

      // The "Iframe Form" category filter from embedded-conversions resolveCategory():
      // rows where providerCategory = 'form' explicitly, OR where providerCategory is
      // null and formProvider is one of the form-provider keys.
      const iframeFormFilter = {
        OR: [
          { providerCategory: "form" },
          { providerCategory: null, formProvider: { in: FORM_PROVIDERS } },
        ],
      };

      // Canonical WHERE -- mirrors embedded-conversions baseWhere exactly,
      // scoped to a single day instead of a rolling N-day window.
      const canonicalWhere = {
        brandId,
        timestamp: { gte: dayStart, lte: dayEnd },
        isDuplicateOf: null,
        invalidatedReason: null,
        formProvider: { not: null as string | null },
        ...iframeFormFilter,
      };

      // With-dupes WHERE -- same but isDuplicateOf unrestricted.
      const withDupesWhere = {
        brandId,
        timestamp: { gte: dayStart, lte: dayEnd },
        invalidatedReason: null,
        formProvider: { not: null as string | null },
        ...iframeFormFilter,
      };

      const activeWhere = includeDupes ? withDupesWhere : canonicalWhere;

      const [rows, totalIncDupes, markedDupes] = await Promise.all([
        // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
        prisma.siteConversion.findMany({
          where: activeWhere,
          select: {
            id: true,
            sessionId: true,
            visitorId: true,
            formId: true,
            formProvider: true,
            providerCategory: true,
            pageUrl: true,
            conversionType: true,
            isDuplicateOf: true,
            timestamp: true,
          },
          orderBy: [{ sessionId: "asc" }, { timestamp: "asc" }],
        }),
        // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
        prisma.siteConversion.count({ where: withDupesWhere }),
        // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
        prisma.siteConversion.count({
          where: { ...withDupesWhere, isDuplicateOf: { not: null } },
        }),
      ]);

      // Session clustering stats
      const sessionCounts: Record<string, number> = {};
      for (const r of rows) {
        const k = hash(r.sessionId);
        sessionCounts[k] = (sessionCounts[k] ?? 0) + 1;
      }
      const sessionsWithMultiple = Object.values(sessionCounts).filter((c) => c > 1).length;
      const maxPerSession = Object.values(sessionCounts).reduce((m, c) => Math.max(m, c), 0);

      // ConversionType breakdown
      const typeCounts: Record<string, number> = {};
      for (const r of rows) {
        typeCounts[r.conversionType] = (typeCounts[r.conversionType] ?? 0) + 1;
      }

      // Provider breakdown
      const providerCounts: Record<string, number> = {};
      for (const r of rows) {
        const p = r.formProvider ?? "null";
        providerCounts[p] = (providerCounts[p] ?? 0) + 1;
      }

      // Unique stripped URLs
      const urlCounts: Record<string, number> = {};
      for (const r of rows) {
        const u = stripTrackingParams(r.pageUrl);
        urlCounts[u] = (urlCounts[u] ?? 0) + 1;
      }
      const topUrls = Object.entries(urlCounts)
        .sort((a, b) => b[1] - a[1])
        .slice(0, 10)
        .map(([url, count]) => ({ url, count }));

      const mapped = rows.map((r) => ({
        sessionId:        hash(r.sessionId),
        visitorId:        hash(r.visitorId),
        formId:           r.formId ?? null,
        formProvider:     r.formProvider ?? null,
        providerCategory: r.providerCategory ?? null,
        pageUrl:          stripTrackingParams(r.pageUrl),
        conversionType:   r.conversionType,
        isDuplicateOf:    r.isDuplicateOf ? hash(r.isDuplicateOf) : null,
        timestamp:        r.timestamp.toISOString(),
      }));

      return {
        mode: "rows",
        query: { brandId, date: dateParam, includeDupes },
        summary: {
          canonicalRows:            rows.length,
          totalIncDupes,
          markedDupes,
          uniqueSessions:           Object.keys(sessionCounts).length,
          sessionsWithMultiple,
          maxConversionsPerSession: maxPerSession,
          byConversionType:         typeCounts,
          providerBreakdown:        providerCounts,
          topUrls,
        },
        rows: mapped,
        note: "sessionId and visitorId are HMAC-SHA256 hashed with a per-request salt. Rows sharing the same hash share the same underlying id. Hashes are NOT stable across requests.",
      };
    }),
  );
}
