import { eq, and } from "drizzle-orm";
import { db } from "~/server/db";
import { exposures, alerts, subscriptions } from "~/server/db/schema";

export function severityScore(exposure: {
  source: string;
  dataType: string;
}): "info" | "warning" | "critical" {
  const criticalSources = new Set(["hibp"]);
  const warningSources = new Set(["shodan", "censys", "darkWebForum"]);
  const criticalTypes = new Set(["ssn"]);
  const warningTypes = new Set(["email", "phoneNumber"]);

  if (criticalSources.has(exposure.source) || criticalTypes.has(exposure.dataType)) {
    return "critical";
  }
  if (warningSources.has(exposure.source) || warningTypes.has(exposure.dataType)) {
    return "warning";
  }
  return "info";
}

export async function processExposure(newExposure: {
  subscriptionId: string;
  watchlistItemId?: string | null;
  source: string;
  dataType: string;
  identifier: string;
  identifierHash: string;
  severity: string;
  metadata?: Record<string, unknown> | null;
  detectedAt: Date;
}): Promise<{ exposureId: string; alertCreated: boolean }> {
  const severity = newExposure.severity as "info" | "warning" | "critical";

  const [existing] = await db
    .select()
    .from(exposures)
    .where(
      and(
        eq(exposures.identifierHash, newExposure.identifierHash),
        eq(exposures.source, newExposure.source as "hibp" | "securityTrails" | "censys" | "darkWebForum" | "shodan" | "honeypot"),
      ),
    )
    .limit(1);

  if (existing) {
    const currentSeverityIdx = ["info", "warning", "critical"].indexOf(existing.severity);
    const newSeverityIdx = ["info", "warning", "critical"].indexOf(severity);
    if (newSeverityIdx <= currentSeverityIdx) {
      return { exposureId: existing.id, alertCreated: false };
    }
    const [updated] = await db
      .update(exposures)
      .set({
        severity,
        metadata: newExposure.metadata ?? existing.metadata,
        detectedAt: newExposure.detectedAt,
        updatedAt: new Date(),
      })
      .where(eq(exposures.id, existing.id))
      .returning();
    await createAlertForExposure(updated, severity);
    return { exposureId: updated.id, alertCreated: true };
  }

  const [inserted] = await db
    .insert(exposures)
    .values({
      subscriptionId: newExposure.subscriptionId,
      watchlistItemId: newExposure.watchlistItemId ?? null,
      source: newExposure.source as "hibp" | "securityTrails" | "censys" | "darkWebForum" | "shodan" | "honeypot",
      dataType: newExposure.dataType as "email" | "phoneNumber" | "ssn" | "address" | "domain",
      identifier: newExposure.identifier,
      identifierHash: newExposure.identifierHash,
      severity,
      metadata: newExposure.metadata ?? null,
      isFirstTime: true,
      detectedAt: newExposure.detectedAt,
    })
    .returning();

  await createAlertForExposure(inserted, severity);
  return { exposureId: inserted.id, alertCreated: true };
}

async function createAlertForExposure(
  exposure: { id: string; subscriptionId: string; severity: string; dataType: string; source: string; identifier: string },
  severity: "info" | "warning" | "critical",
): Promise<void> {
  const alertSeverityMap: Record<string, "info" | "warning" | "critical"> = {
    info: "info",
    warning: "warning",
    critical: "critical",
  };

  const title = `${severity === "critical" ? "Critical" : severity === "warning" ? "Warning" : "Info"} exposure detected`;
  const message = `${exposure.dataType} exposed on ${exposure.source}: ${exposure.identifier}`;

  const [sub] = await db
    .select()
    .from(subscriptions)
    .where(eq(subscriptions.id, exposure.subscriptionId))
    .limit(1);

  if (!sub) return;

  await db.insert(alerts).values({
    subscriptionId: exposure.subscriptionId,
    userId: sub.userId,
    exposureId: exposure.id,
    type: "exposure_detected",
    title,
    message,
    severity: alertSeverityMap[severity] ?? "info",
    channel: ["email", "push"],
  });
}
