const { createHash } = require('crypto')
const { readdirSync, readFileSync, statSync, writeFileSync } = require('fs')
const { join } = require('path')

const getRevision = filePath => createHash('md5').update(readFileSync(filePath)).digest('hex')
const walkSync = dir => readdirSync(dir, { withFileTypes: true }).flatMap(file =>
  file.isDirectory() ? walkSync(join(dir, file.name)) : join(dir, file.name))

function formatBytes (bytes, decimals = 2) {
  if (bytes === 0) {
    return '0 B'
  }

  const k = 1024
  const sizes = ['B', 'KB', 'MB']

  const i = Math.floor(Math.log(bytes) / Math.log(k))
  const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))

  return `${formattedSize} ${sizes[i]}`
}

function generatePrecacheManifest () {
  const manifest = []
  let size = 0

  const addToManifest = (filePath, url, s) => {
    const revision = getRevision(filePath)
    manifest.push({ url, revision })
    size += s
  }

  const staticDir = join(__dirname, '../public')
  const staticFiles = walkSync(staticDir)
  const staticMatch = f => [/\.(gif|jpe?g|ico|png|ttf|woff|woff2)$/].some(m => m.test(f))
  staticFiles.filter(staticMatch).forEach(file => {
    const stats = statSync(file)
    addToManifest(file, file.slice(staticDir.length), stats.size)
  })

  const pagesDir = join(__dirname, '../pages')
  const precacheURLs = ['/offline']
  const pagesFiles = walkSync(pagesDir)
  const fileToUrl = f => f.slice(pagesDir.length).replace(/\.js$/, '')
  const pageMatch = f => precacheURLs.some(url => fileToUrl(f) === url)
  pagesFiles.filter(pageMatch).forEach(file => {
    const stats = statSync(file)
    // This is not ideal since dependencies of the pages may have changed
    // but we would still generate the same revision ...
    // The ideal solution would be to create a revision from the file generated by webpack
    // in .next/server/pages but the file may not exist yet when we run this script
    addToManifest(file, fileToUrl(file), stats.size)
  })

  const output = 'sw/precache-manifest.json'
  writeFileSync(output, JSON.stringify(manifest, null, 2))

  console.log(`Created precache manifest at ${output}. Cache will include ${manifest.length} URLs with a size of ${formatBytes(size)}.`)
}

module.exports = { generatePrecacheManifest }