import { readFile } from 'node:fs/promises'
import path from 'node:path'
import semver from 'semver'
import {
  isSelfContainedBinary,
  resolveBinaryUpdateConfig,
  resolveBunTarget,
} from './binary-upgrade'
import { getDimcodeUserDir } from './user-cache'
import { resolveCliVersion } from './version'

type BinaryUpgradeMeta = Readonly<{
  cliVersion: string
  binaryPath: string
}>

export async function printCliVersionInfo(
  env: Record<string, string | undefined> = process.env,
): Promise<void> {
  const resolved = resolveCliVersion()
  const version = resolved?.version || 'unknown'
  const source = resolved?.source || 'unknown'
  const execPath = safeStr(process.execPath)
  const standalone = isSelfContainedBinary()

  console.log(`dim ${version} (${source})`)
  console.log(`execPath: ${execPath || 'unknown'}`)
  console.log(`standalone: ${standalone}`)

  if (standalone) {
    const target = resolveBunTarget() || 'unknown'
    const cfg = resolveBinaryUpdateConfig(env)
    console.log(`target: ${target}`)
    console.log(
      `update: provider=${cfg.provider} project=${cfg.project}${cfg.host ? ` host=${cfg.host}` : ''}`,
    )
    console.log(`update: githubApiBaseUrl=${cfg.githubApiBaseUrl}`)

    const meta = await readBinaryUpgradeMeta(env)
    if (meta) {
      console.log(`installed: ${meta.cliVersion} at ${meta.binaryPath}`)
      if (semver.valid(meta.cliVersion) && semver.valid(version)) {
        console.log(`installedIsNewer: ${semver.gt(meta.cliVersion, version)}`)
      }
    }
    else {
      console.log('installed: (none)')
    }
  }
}

async function readBinaryUpgradeMeta(
  env: Record<string, string | undefined>,
): Promise<BinaryUpgradeMeta | null> {
  try {
    const userDir = await getDimcodeUserDir(env)
    const filePath = path.join(userDir, 'bin', 'binary-upgrade.json')
    const raw = await readFile(filePath, 'utf8')
    return parseBinaryUpgradeMeta(JSON.parse(raw))
  }
  catch {
    return null
  }
}

function parseBinaryUpgradeMeta(value: unknown): BinaryUpgradeMeta | null {
  if (!isRecord(value) || value.version !== 1)
    return null

  const cliVersion = typeof value.cliVersion === 'string' ? value.cliVersion : ''
  const binaryPath = typeof value.binaryPath === 'string' ? value.binaryPath : ''
  if (!cliVersion || !binaryPath)
    return null

  return { cliVersion, binaryPath }
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return value != null && typeof value === 'object' && !Array.isArray(value)
}

function safeStr(v: unknown): string | null {
  const s = String(v ?? '').trim()
  return s || null
}
