import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises'
import { spawn } from 'node:child_process'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'

const scriptDir = dirname(fileURLToPath(import.meta.url))
const packageDir = dirname(scriptDir)
const packageJson = JSON.parse(
  await readFile(join(packageDir, 'package.json'), 'utf8'),
)

const version = packageJson.version
const baseUrl =
  process.env.TURING_DOWNLOAD_BASE_URL ??
  `https://tap365.cn/turing/v${version}`

const targets = {
  linux: {
    x64: {
      archiveName: `turing-v${version}-linux-x64-max.tar.gz`,
      localName: 'turing',
    },
  },
  darwin: {
    x64: {
      archiveName: `turing-v${version}-macos-x64.tar.gz`,
      localName: 'turing',
    },
    arm64: {
      archiveName: `turing-v${version}-macos-arm64.tar.gz`,
      localName: 'turing',
    },
  },
  win32: {
    x64: {
      remoteName: `turing-v${version}-windows-x64.exe`,
      localName: 'turing.exe',
    },
  },
}

const target = targets[process.platform]?.[process.arch]

if (!target) {
  console.error(
    `错误：当前 npm 安装包不支持 ${process.platform}/${process.arch}。`,
  )
  process.exit(1)
}

const vendorDir = join(packageDir, 'vendor')
const localPath = join(vendorDir, target.localName)
const archiveUrl = target.archiveName ? `${baseUrl}/${target.archiveName}` : null
const binaryUrl = target.remoteName ? `${baseUrl}/${target.remoteName}` : null

await mkdir(vendorDir, { recursive: true })

function formatSize(bytes) {
  if (!Number.isFinite(bytes) || bytes <= 0) {
    return '未知大小'
  }

  const mib = bytes / (1024 * 1024)
  if (mib >= 1024) {
    return `${(mib / 1024).toFixed(1)} GiB`
  }

  return `${mib.toFixed(1)} MiB`
}

async function readResponseBuffer(response, sourceUrl) {
  if (!response.body) {
    console.error(`错误：下载响应没有 body：${sourceUrl}`)
    process.exit(1)
  }

  const total = Number(response.headers.get('content-length') || '0')
  const reader = response.body.getReader()
  const chunks = []
  let received = 0
  let lastLoggedAt = 0

  while (true) {
    const { done, value } = await reader.read()
    if (done) {
      break
    }

    chunks.push(Buffer.from(value))
    received += value.byteLength

    // 下载大文件时给出周期性进度，避免用户误判成卡死。
    if (received - lastLoggedAt >= 1024 * 1024) {
      lastLoggedAt = received
      if (total > 0) {
        const percent = ((received / total) * 100).toFixed(1)
        console.log(`下载中：${percent}%（${formatSize(received)} / ${formatSize(total)}）`)
      } else {
        console.log(`下载中：${formatSize(received)}`)
      }
    }
  }

  if (total > 0) {
    console.log(`下载完成：${formatSize(received)} / ${formatSize(total)}`)
  } else {
    console.log(`下载完成：${formatSize(received)}`)
  }

  return Buffer.concat(chunks)
}

async function extractTarGz(buffer, destDir) {
  await new Promise((resolve, reject) => {
    const child = spawn('tar', ['-xzf', '-', '-C', destDir], {
      stdio: ['pipe', 'inherit', 'inherit'],
    })

    child.on('error', reject)
    child.on('exit', code => {
      if (code === 0) {
        resolve()
        return
      }

      reject(new Error(`tar 解压失败，退出码 ${code ?? 'unknown'}`))
    })

    child.stdin.end(buffer)
  })
}

const sourceUrl = archiveUrl ?? binaryUrl
if (!sourceUrl) {
  console.error(`错误：当前平台 ${process.platform}/${process.arch} 缺少下载源配置。`)
  process.exit(1)
}

console.log(`开始下载 Turing Code：${sourceUrl}`)

const response = await fetch(sourceUrl)
if (!response.ok) {
  console.error(`错误：下载二进制失败：${sourceUrl} -> HTTP ${response.status}`)
  process.exit(1)
}

const payload = await readResponseBuffer(response, sourceUrl)

if (archiveUrl) {
  await extractTarGz(payload, vendorDir)
} else {
  await writeFile(localPath, payload)
}

if (process.platform !== 'win32') {
  await chmod(localPath, 0o755)
}

console.log(`已准备好 Turing Code 二进制：${localPath}`)
