import { prisma } from "@nexus/db";

/** Window over which auth events are analysed. */
const WINDOW_MS = 30 * 60 * 1000; // 30 minutes

/**
 * Alert thresholds — tune per deployment if needed.
 * Exported so tests can reference them without re-declaring magic numbers.
 */
export const THRESHOLDS = {
  /** Total failed login attempts in the window before alerting. */
  globalFailures: 20,
  /** Failed attempts attributed to a single entityId (userId / IP placeholder) before alerting. */
  perEntityFailures: 10,
};

export interface AnomalyReport {
  windowStartedAt: string;
  windowEndedAt: string;
  totalFailures: number;
  anomalies: Array<{ type: string; count: number; entityId: string | null }>;
}

/**
 * Analyses recent auth audit events and returns detected anomalies.
 * Exported for unit testing without an HTTP layer.
 */
export async function detectAuthAnomalies(windowMs = WINDOW_MS): Promise<AnomalyReport> {
  const windowEnd = new Date();
  const windowStart = new Date(windowEnd.getTime() - windowMs);

  const failureEvents = await prisma.auditLog.findMany({
    where: {
      entityType: "Auth",
      action: "CREATE",
      summary: { startsWith: "Login failed" },
      createdAt: { gte: windowStart, lte: windowEnd },
    },
    select: {
      entityId: true,
      summary: true,
    },
  });

  const anomalies: AnomalyReport["anomalies"] = [];

  // Global threshold: too many failures overall
  if (failureEvents.length >= THRESHOLDS.globalFailures) {
    anomalies.push({
      type: "HIGH_GLOBAL_FAILURE_RATE",
      count: failureEvents.length,
      entityId: null,
    });
  }

  // Per-entity threshold: one entity accumulating failures (brute-force pattern)
  const countByEntity = new Map<string, number>();
  for (const event of failureEvents) {
    if (event.entityId) {
      countByEntity.set(event.entityId, (countByEntity.get(event.entityId) ?? 0) + 1);
    }
  }
  for (const [entityId, count] of countByEntity.entries()) {
    if (count >= THRESHOLDS.perEntityFailures) {
      anomalies.push({
        type: "CONCENTRATED_FAILURES",
        count,
        entityId,
      });
    }
  }

  return {
    windowStartedAt: windowStart.toISOString(),
    windowEndedAt: windowEnd.toISOString(),
    totalFailures: failureEvents.length,
    anomalies,
  };
}
