import {
  cp,
  mkdir,
  readFile,
  readdir,
  rm,
  stat,
  writeFile,
} from "node:fs/promises"
import path from "node:path"
import { zipSync } from "fflate"

const root = globalThis.process.cwd()
const dist = path.join(root, "dist")
const output = path.join(root, "pages-dist")

if (path.basename(root) !== "billiards") {
  throw new Error("Run this script from the billiards project root")
}

await rm(output, { recursive: true, force: true })
await mkdir(output, { recursive: true })

for (const directory of ["assets", "css", "images", "models", "sounds"]) {
  await cp(path.join(dist, directory), path.join(output, directory), {
    recursive: true,
  })
}

for (const filename of [
  "_headers",
  "favicon.png",
  "help.html",
  "index.html",
  "index.js",
  "manifest.json",
  "source.html",
  "three_core.js",
  "three_examples.js",
  "three_module.js",
]) {
  await cp(path.join(dist, filename), path.join(output, filename))
}

await cp(path.join(root, "LICENSE"), path.join(output, "LICENSE.txt"))
await cp(
  path.join(root, "MODIFICATIONS.md"),
  path.join(output, "MODIFICATIONS.md")
)
await cp(path.join(root, "README.md"), path.join(output, "UPSTREAM-README.md"))

const archive = {}
const sourceEntries = [
  ".stylelintrc.json",
  "AGENTS.md",
  "LICENSE",
  "MODIFICATIONS.md",
  "README.md",
  "dist",
  "eslint.config.mjs",
  "package-lock.json",
  "package.json",
  "prepare-pages.mjs",
  "src",
  "test",
  "tsconfig.json",
  "webpack.config.js",
  "yarn.lock",
]

async function addToArchive(absolutePath) {
  const metadata = await stat(absolutePath)
  if (metadata.isDirectory()) {
    for (const entry of await readdir(absolutePath)) {
      await addToArchive(path.join(absolutePath, entry))
    }
    return
  }

  const relativePath = path.relative(root, absolutePath).replaceAll("\\", "/")
  archive[`billiards-source/${relativePath}`] = new Uint8Array(
    await readFile(absolutePath)
  )
}

for (const entry of sourceEntries) {
  await addToArchive(path.join(root, entry))
}

const sourceDirectory = path.join(output, "source")
await mkdir(sourceDirectory, { recursive: true })
const sourceArchive = zipSync(archive, { level: 6 })
const maxPagesFileSize = 25 * 1024 * 1024
if (sourceArchive.byteLength > maxPagesFileSize) {
  throw new Error(
    `Source archive is ${(sourceArchive.byteLength / 1024 / 1024).toFixed(2)} MiB, over the Cloudflare Pages 25 MiB limit`
  )
}
await writeFile(
  path.join(sourceDirectory, "billiards-source.zip"),
  sourceArchive
)

const deployedFiles = []
async function collectDeploymentFiles(directory) {
  for (const entry of await readdir(directory, { withFileTypes: true })) {
    const absolutePath = path.join(directory, entry.name)
    if (entry.isDirectory()) {
      await collectDeploymentFiles(absolutePath)
    } else {
      deployedFiles.push({
        path: path.relative(output, absolutePath).replaceAll("\\", "/"),
        size: (await stat(absolutePath)).size,
      })
    }
  }
}

await collectDeploymentFiles(output)
const totalBytes = deployedFiles.reduce((sum, file) => sum + file.size, 0)
const largest = deployedFiles.reduce(
  (current, file) => (file.size > current.size ? file : current),
  { path: "", size: -1 }
)

globalThis.console.log(
  JSON.stringify(
    {
      output,
      files: deployedFiles.length,
      totalMiB: Number((totalBytes / 1024 / 1024).toFixed(2)),
      largestFile: largest.path,
      largestMiB: Number((largest.size / 1024 / 1024).toFixed(2)),
    },
    null,
    2
  )
)
