import { PrismaClient } from '@prisma/client'
import os from 'node:os'

const globalKey = '__dlbdi_prisma_client__'

function ensureDatabaseUrl() {
  if (process.env.DATABASE_URL) return
  const host = process.env.PGHOST || process.env.POSTGRES_HOST || 'localhost'
  const port = process.env.PGPORT || process.env.POSTGRES_PORT || '5432'
  const database = process.env.PGDATABASE || process.env.DATABASE_NAME || process.env.POSTGRES_DB || 'dlbdi_dev'
  const user = process.env.PGUSER || process.env.POSTGRES_USER || os.userInfo().username || 'postgres'
  const password = process.env.PGPASSWORD || process.env.POSTGRES_PASSWORD || ''
  const auth = password
    ? `${encodeURIComponent(user)}:${encodeURIComponent(password)}`
    : encodeURIComponent(user)
  process.env.DATABASE_URL = `postgresql://${auth}@${host}:${port}/${database}?schema=public`
}

function createPrismaClient() {
  ensureDatabaseUrl()
  return new PrismaClient({
    log: process.env.PRISMA_QUERY_LOG === 'true'
      ? ['query', 'error', 'warn']
      : ['error', 'warn'],
  })
}

export function prismaClient() {
  if (!globalThis[globalKey]) globalThis[globalKey] = createPrismaClient()
  return globalThis[globalKey]
}

export async function withPrismaTransaction(action, options = {}) {
  const prisma = prismaClient()
  return prisma.$transaction((tx) => action(tx), {
    timeout: Number(options.timeoutMs || process.env.PRISMA_TRANSACTION_TIMEOUT_MS || 10_000),
    maxWait: Number(options.maxWaitMs || process.env.PRISMA_TRANSACTION_MAX_WAIT_MS || 5_000),
  })
}

export function pageArgs({ page = 1, pageSize = 50 } = {}) {
  const safePage = Math.max(1, Number(page) || 1)
  const safePageSize = Math.min(100, Math.max(1, Number(pageSize) || 50))
  return {
    page: safePage,
    pageSize: safePageSize,
    skip: (safePage - 1) * safePageSize,
    take: safePageSize,
  }
}

export function prismaJson(value, fallback = {}) {
  if (value === null || value === undefined) return fallback
  if (typeof value === 'object') return value
  try {
    return JSON.parse(String(value))
  } catch {
    return fallback
  }
}
