import { db } from "@/lib/db";
import { security_events } from "@/lib/schema/db";
import { hashIp, newId } from "./tokens";

/**
 * Security audit logging.
 *
 * Append-only record of security-relevant events: failed logins, password
 * changes, admin actions, unusual API activity. The write is deliberately
 * best-effort — logging must never take the request down with it — so every
 * public function swallows its own errors and logs to the server console
 * instead of throwing.
 *
 * Event types are free-form strings so new events never require a migration,
 * but the constants below keep the vocabulary stable and greppable.
 */

export const SecurityEventType = {
  LOGIN_FAILED: "login.failed",
  LOGIN_SUCCESS: "login.success",
  LOGIN_LOCKED: "login.locked",
  LOGIN_RATE_LIMITED: "login.rate_limited",
  LOGOUT: "auth.logout",
  SIGNUP: "signup.created",
  SIGNUP_FAILED: "signup.failed",
  EMAIL_VERIFIED: "email.verified",
  EMAIL_VERIFICATION_RESENT: "email.verification_resent",
  PASSWORD_RESET_REQUESTED: "password.reset_requested",
  PASSWORD_RESET_COMPLETED: "password.reset_completed",
  PASSWORD_CHANGED: "password.changed",
  ACCOUNT_DELETED: "account.deleted",
  GOOGLE_SIGNIN_STARTED: "google.signin_started",
  GOOGLE_SIGNIN_SUCCESS: "google.signin_success",
  GOOGLE_SIGNIN_FAILED: "google.signin_failed",
  APPLE_SIGNIN_SUCCESS: "apple.signin_success",
  APPLE_SIGNIN_FAILED: "apple.signin_failed",
  ADMIN_ACTION: "admin.action",
  API_UNUSUAL: "api.unusual",
} as const;

export type SecurityEventType = (typeof SecurityEventType)[keyof typeof SecurityEventType];

export interface SecurityEventInput {
  type: SecurityEventType | string;
  userId?: string | null;
  /** Normalised email of the affected or targeted account, when known. */
  email?: string | null;
  /** Raw IP — hashed before storage; never persisted in plain text. */
  ip?: string | null;
  userAgent?: string | null;
  /** Free-form extras: endpoint, attempt counts, admin route, … */
  metadata?: Record<string, unknown> | null;
}

/**
 * Persists one security event. Never throws: any failure is logged to the
 * server console so monitoring can still spot that the audit trail is down.
 */
export async function logSecurityEvent(input: SecurityEventInput): Promise<void> {
  try {
    await db.insert(security_events).values({
      id: newId("evt"),
      type: input.type,
      userId: input.userId ?? null,
      email: input.email ?? null,
      ipHash: hashIp(input.ip ?? null),
      userAgent: input.userAgent?.slice(0, 255) ?? null,
      metadataJson: input.metadata ?? null,
    });
  } catch (error) {
    // The audit trail must never break the request it is recording.
    console.error("[security] failed to write security event", error);
  }
}

/**
 * Fire-and-forget variant for request handlers that must not await a database
 * write on the hot path. Ordering is not guaranteed relative to the response.
 */
export function logSecurityEventAsync(input: SecurityEventInput): void {
  void logSecurityEvent(input);
}

/**
 * Lists the most recent security events, newest first. Used by the admin
 * console and by tests; the optional `type` filter narrows the result.
 */
export async function listRecentSecurityEvents(
  limit = 100,
  type?: string
): Promise<Array<{ type: string; email: string | null; createdAt: Date; metadata: unknown }>> {
  try {
    const { desc, eq } = await import("drizzle-orm");
    const rows = await db
      .select()
      .from(security_events)
      .where(type ? eq(security_events.type, type) : undefined)
      .orderBy(desc(security_events.createdAt))
      .limit(limit);

    return rows.map((row) => ({
      type: row.type,
      email: row.email,
      createdAt: row.createdAt,
      metadata: row.metadataJson,
    }));
  } catch (error) {
    console.error("[security] failed to list security events", error);
    return [];
  }
}
