import { createHash } from 'node:crypto'
import { createWriteStream } from 'node:fs'
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises'
import path from 'node:path'

export async function readJson(filePath) {
  return JSON.parse(await readFile(filePath, 'utf8'))
}

export async function writeJson(filePath, value) {
  await mkdir(path.dirname(filePath), { recursive: true })
  await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8')
}

export async function exists(filePath) {
  try {
    await stat(filePath)
    return true
  } catch {
    return false
  }
}

export async function downloadFile(url, filePath) {
  await mkdir(path.dirname(filePath), { recursive: true })
  const response = await fetch(url)
  if (!response.ok || !response.body) {
    throw new Error(`Download failed ${response.status} ${response.statusText}: ${url}`)
  }

  const output = createWriteStream(filePath)
  await new Promise((resolve, reject) => {
    response.body.pipeTo(
      new WritableStream({
        write(chunk) {
          output.write(Buffer.from(chunk))
        },
        close() {
          output.end(resolve)
        },
        abort(reason) {
          output.destroy(reason)
          reject(reason)
        },
      }),
    ).catch(reject)
  })
}

export async function sha256File(filePath) {
  const data = await readFile(filePath)
  return createHash('sha256').update(data).digest('hex')
}

export function normalizeStrong(value) {
  const match = /^([GH])0*([0-9]+)$/i.exec(value)
  return match ? `${match[1].toUpperCase()}${Number(match[2])}` : value.toUpperCase()
}

export function cleanUsfmText(input) {
  return input
    .replace(/â€¦/g, '...')
    .replace(/…/g, '...')
    .replace(/â€™/g, "'")
    .replace(/â€œ/g, '"')
    .replace(/â€/g, '"')
    .replace(/\\f [\s\S]*?\\f\*/g, '')
    .replace(/\\x [\s\S]*?\\x\*/g, '')
    .replace(/\\add\s+([^\\]+)\\add\*/g, '$1')
    .replace(/\\nd\s+([\s\S]*?)\\nd\*/g, '$1')
    .replace(/\\[+\w]+\*/g, '')
    .replace(/\\[a-z0-9+]+(?:\s+)?/gi, '')
    .replace(/\s+/g, ' ')
    .replace(/\s+([,.;:?!])/g, '$1')
    .trim()
}
