import { Prisma, PrismaClient } from '@prisma/client';
import { prisma } from '../database';

export type DbClient = PrismaClient;
export type TxClient = Prisma.TransactionClient;

function isRootClient(client: DbClient | TxClient): client is DbClient {
  return typeof (client as DbClient).$transaction === 'function';
}

/**
 * Runs `fn` inside a DB transaction.
 *
 * - If `fn` throws/rejects, **all operations are rolled back**.
 * - Prefer this over array-based transactions when you need multiple steps
 *   (reads + conditional writes) to be atomic.
 */
export async function transaction<T>(
  fn: (tx: TxClient) => Promise<T>,
  options?: {
    maxWait?: number;
    timeout?: number;
    isolationLevel?: Prisma.TransactionIsolationLevel;
  },
): Promise<T> {
  return prisma.$transaction(fn, options);
}

/**
 * Utility to support both "already in a transaction" and "start a new one".
 *
 * If `client` is a root PrismaClient, it starts a transaction.
 * If `client` is already a TransactionClient, it reuses it.
 */
export async function withTransaction<T>(
  client: DbClient | TxClient,
  fn: (tx: TxClient) => Promise<T>,
  options?: {
    maxWait?: number;
    timeout?: number;
    isolationLevel?: Prisma.TransactionIsolationLevel;
  },
): Promise<T> {
  if (isRootClient(client)) {
    return client.$transaction(fn, options);
  }
  return fn(client);
}

