import { createRequire } from 'node:module'
import { createEnvReader, readProcessEnv } from '../env'
import { perfLog } from './perf-log'

interface GcEntryLike {
  entryType?: string
  name?: string
  duration?: number
  kind?: number
}

let started = false

function env() {
  return createEnvReader(readProcessEnv())
}

function parseEnabled(v: unknown): boolean {
  const s = String(v ?? '')
    .trim()
    .toLowerCase()
  return s === '1' || s === 'true' || s === 'yes' || s === 'on'
}

function thresholdMs(): number {
  const raw = Number(env().prefixedStr('GC_LOG_SLOW_MS'))
  if (Number.isFinite(raw) && raw > 0)
    return Math.max(1, Math.floor(raw))
  return 20
}

function kindName(kind: number | undefined): string {
  // Node's perf_hooks uses numeric GC kinds (minor/major/incremental/weakcb), but Bun may differ.
  // We keep a best-effort mapping without importing constants.
  switch (Number(kind)) {
    case 1:
      return 'minor'
    case 2:
      return 'major'
    case 4:
      return 'incremental'
    case 8:
      return 'weakcb'
    default:
      return String(kind ?? '')
  }
}

export function startGcLogger(): void {
  if (started)
    return
  started = true

  const e = env()
  if (!parseEnabled(e.prefixedStr('GC_LOG')))
    return

  const require = createRequire(import.meta.url)
  let PerformanceObserver: any
  try {
    ;({ PerformanceObserver } = require('node:perf_hooks'))
  }
  catch {
    return
  }

  try {
    const slowMs = thresholdMs()
    const obs = new PerformanceObserver((list: any) => {
      try {
        for (const entry of list?.getEntries?.() ?? []) {
          const e2 = entry as GcEntryLike
          const dur = Number(e2.duration ?? 0)
          if (!Number.isFinite(dur) || dur < slowMs)
            continue
          perfLog(
            `[GC] ${dur.toFixed(1)}ms kind=${kindName(e2.kind)} name=${String(e2.name ?? '')}`,
          )
        }
      }
      catch {
        // ignore
      }
    })
    obs.observe({ entryTypes: ['gc'] })
  }
  catch {
    // ignore
  }
}
