import { execFileSync } from 'node:child_process'

export interface AppBuildMetadata {
  commitHash: string | null
  isDirty: boolean
}

interface ResolveAppBuildMetadataOptions {
  cwd?: string
  env?: NodeJS.ProcessEnv
  runGitCommand?: (cwd: string, args: string[]) => string
}

const DIRTY_ENV_TRUE_VALUES = new Set(['1', 'true', 'yes', 'dirty'])
const DIRTY_ENV_FALSE_VALUES = new Set(['0', 'false', 'no', 'clean'])

function normalizeCommitHash(value: string | undefined): string | null {
  if (typeof value !== 'string') return null

  const trimmed = value.trim()
  return trimmed.length > 0 ? trimmed : null
}

function parseDirtyEnvValue(value: string | undefined): boolean | null {
  if (typeof value !== 'string') return null

  const normalized = value.trim().toLowerCase()
  if (!normalized) return null
  if (DIRTY_ENV_TRUE_VALUES.has(normalized)) return true
  if (DIRTY_ENV_FALSE_VALUES.has(normalized)) return false
  return null
}

function execGitCommand(cwd: string, args: string[]): string {
  return execFileSync('git', args, {
    cwd,
    encoding: 'utf8',
    stdio: ['ignore', 'pipe', 'ignore'],
  })
}

function resolveGitCommitHash(
  cwd: string,
  runGitCommand: (cwd: string, args: string[]) => string,
): string | null {
  try {
    return normalizeCommitHash(runGitCommand(cwd, ['rev-parse', 'HEAD']))
  } catch {
    return null
  }
}

function resolveGitDirtyState(cwd: string, runGitCommand: (cwd: string, args: string[]) => string): boolean {
  try {
    return runGitCommand(cwd, ['status', '--porcelain']).trim().length > 0
  } catch {
    return false
  }
}

export function resolveAppBuildMetadata(options: ResolveAppBuildMetadataOptions = {}): AppBuildMetadata {
  const cwd = options.cwd ?? process.cwd()
  const env = options.env ?? process.env
  const runGitCommand = options.runGitCommand ?? execGitCommand

  const envCommitHash = normalizeCommitHash(env.PRISM_GIT_COMMIT)
  const envDirtyState = parseDirtyEnvValue(env.PRISM_GIT_DIRTY)

  if (envCommitHash) {
    return {
      commitHash: envCommitHash,
      isDirty: envDirtyState ?? false,
    }
  }

  const commitHash = resolveGitCommitHash(cwd, runGitCommand)
  if (!commitHash) {
    return {
      commitHash: null,
      isDirty: false,
    }
  }

  return {
    commitHash,
    isDirty: envDirtyState ?? resolveGitDirtyState(cwd, runGitCommand),
  }
}
