import { FastifyRequest } from "fastify";
import { Prisma } from "@prisma/client";
import prisma from "../config/database";
import { AuditAction, EntityType, AuthData } from "../types";

/**
 * Safe JSON.stringify replacer that handles Prisma Decimal and BigInt
 * by converting them to strings. Prevents JSON.stringify from throwing
 * on values that include these types.
 */
function safeJsonReplacer(_key: string, value: unknown): unknown {
  if (typeof value === "bigint") return String(value);
  // Precise Decimal detection via instanceof (Prisma.Decimal is decimal.js),
  // with a structural fallback for Decimal instances that crossed a module
  // boundary and fail the instanceof check.
  if (value instanceof Prisma.Decimal) return value.toString();
  if (
    value !== null &&
    typeof value === "object" &&
    "toNumber" in value &&
    typeof (value as { toNumber?: unknown }).toNumber === "function" &&
    "toString" in value &&
    typeof (value as { toString?: unknown }).toString === "function"
  ) {
    return (value as { toString(): string }).toString();
  }
  return value;
}

export async function logAudit(params: {
  request: FastifyRequest;
  authData?: AuthData | null;
  action: AuditAction;
  entityType?: EntityType;
  entityId?: number;
  description?: string;
  oldValues?: Record<string, unknown>;
  newValues?: Record<string, unknown>;
}): Promise<void> {
  try {
    await prisma.audit_logs.create({
      data: {
        user_id: params.authData?.userId ?? null,
        username: params.authData?.username ?? null,
        user_ip: params.request.ip,
        action: params.action,
        entity_type: params.entityType ?? null,
        entity_id: params.entityId ?? null,
        description: params.description ?? null,
        old_values: params.oldValues
          ? JSON.stringify(params.oldValues, safeJsonReplacer)
          : null,
        new_values: params.newValues
          ? JSON.stringify(params.newValues, safeJsonReplacer)
          : null,
        user_agent: params.request.headers["user-agent"] ?? null,
        session_id: null,
      },
    });
  } catch (err) {
    console.error(
      "Failed to write audit log:",
      {
        action: params.action,
        entityType: params.entityType,
        entityId: params.entityId,
        description: params.description,
        userId: params.authData?.userId,
      },
      err,
    );
  }
}
