/**
 * Helper transaksi Serializable + retry. Single source of truth supaya semua
 * operasi yang rawan race (join/cancel trip, refund, payment) konsisten:
 * - Isolation level Serializable → Postgres SSI mendeteksi konflik.
 * - Retry otomatis saat serialization failure (Prisma error code `P2034`).
 */
import { Prisma } from "@/app/generated/prisma/client";
import { prisma } from "@/lib/prisma";

const SERIAL_TX_ATTEMPTS = 6;

export function isSerializationConflict(err: unknown): boolean {
  return (
    typeof err === "object" &&
    err !== null &&
    "code" in err &&
    (err as { code: string }).code === "P2034"
  );
}

/**
 * Jalankan `fn` dalam transaksi Serializable. Kalau gagal karena konflik
 * serialisasi, ulang sampai `SERIAL_TX_ATTEMPTS` kali. Error bisnis (non-P2034)
 * langsung dilempar tanpa retry.
 */
export async function runSerializable<T>(
  fn: (tx: Prisma.TransactionClient) => Promise<T>,
  fallbackMessage = "Operasi sedang ramai. Coba lagi sebentar."
): Promise<T> {
  let lastErr: unknown;
  for (let attempt = 0; attempt < SERIAL_TX_ATTEMPTS; attempt++) {
    try {
      return await prisma.$transaction(fn, {
        isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
        maxWait: 5000,
        timeout: 15000,
      });
    } catch (e) {
      lastErr = e;
      if (isSerializationConflict(e) && attempt < SERIAL_TX_ATTEMPTS - 1) {
        continue;
      }
      throw e;
    }
  }
  throw lastErr instanceof Error ? lastErr : new Error(fallbackMessage);
}
