/**
 * Dedup Runner — processes SiteConversion rows for a brand, identifies
 * clusters, and marks duplicates by setting isDuplicateOf to the
 * surviving row's id.
 */

import { prisma } from "@/lib/prisma";
import { clusterDuplicates, type DedupRow } from "./dedup";

export interface DedupResult {
  brandId: string;
  totalRowsProcessed: number;
  duplicatesMarked: number;
  clustersFormed: number;
  durationMs: number;
}

export async function runDedupForBrand(
  brandId: string,
  options: { lookbackDays?: number; dryRun?: boolean } = {},
): Promise<DedupResult> {
  const start = Date.now();
  const lookbackDays = options.lookbackDays ?? 30;
  const since = new Date(Date.now() - lookbackDays * 86_400_000);

  // Fetch all non-duplicate rows in the window
  // intentionally unfiltered: dedup worker; must scan all rows to identify duplicates
  const rows = await prisma.siteConversion.findMany({
    where: {
      brandId,
      timestamp: { gte: since },
      isDuplicateOf: null,
    },
    select: {
      id: true,
      brandId: true,
      visitorId: true,
      sessionId: true,
      conversionType: true,
      pageUrl: true,
      formId: true,
      formFields: true,
      detectionMethod: true,
      phoneNumber: true,
      firstTouchChannel: true,
      lastTouchChannel: true,
      touchpoints: true,
      timestamp: true,
    },
    orderBy: { timestamp: "asc" },
  });

  const clusters = clusterDuplicates(rows as DedupRow[]);

  let duplicatesMarked = 0;

  if (!options.dryRun && clusters.length > 0) {
    // Batch mark duplicates in groups of 100 for DB efficiency
    const updates: Array<{ ids: string[]; keptId: string }> = [];
    for (const cluster of clusters) {
      updates.push({
        ids: cluster.duplicates.map((d) => d.id),
        keptId: cluster.kept.id,
      });
    }

    // Execute in transaction for consistency
    await prisma.$transaction(
      updates.map((u) =>
        prisma.siteConversion.updateMany({
          where: { id: { in: u.ids } },
          data: { isDuplicateOf: u.keptId },
        }),
      ),
    );

    duplicatesMarked = updates.reduce((s, u) => s + u.ids.length, 0);
  } else if (options.dryRun) {
    duplicatesMarked = clusters.reduce((s, c) => s + c.duplicates.length, 0);
  }

  return {
    brandId,
    totalRowsProcessed: rows.length,
    duplicatesMarked,
    clustersFormed: clusters.length,
    durationMs: Date.now() - start,
  };
}

/**
 * Compute dedup quality score for a brand: (unique / total) * 100.
 * Uses the isDuplicateOf column — higher = better (100 = no duplicates).
 */
export async function getDedupQuality(brandId: string, lookbackDays = 14): Promise<{
  quality: number;
  total: number;
  unique: number;
  duplicates: number;
}> {
  const since = new Date(Date.now() - lookbackDays * 86_400_000);
  const [total, duplicates] = await Promise.all([
    // intentionally unfiltered: dedup worker; must scan all rows to identify duplicates
    prisma.siteConversion.count({ where: { brandId, timestamp: { gte: since } } }),
    // intentionally unfiltered: dedup worker; must scan all rows to identify duplicates
    prisma.siteConversion.count({ where: { brandId, timestamp: { gte: since }, isDuplicateOf: { not: null } } }),
  ]);
  const unique = total - duplicates;
  const quality = total > 0 ? Math.round((unique / total) * 100) : 100;
  return { quality, total, unique, duplicates };
}
