/* eslint-disable no-console */
import { copyFile, readdir, readFile, rm, rmdir, writeFile } from 'fs/promises'
import { exit } from 'process'
import { loadPyodide } from 'pyodide'

const PACKAGES = ['micropip', 'jinja2', 'jinja2_strcase', 'six', 'attrs']
const PYODIDE_DIR = 'pyodide/files'
const PYODIDE_PACKAGE_JSON = `${PYODIDE_DIR}/package.json`
const NODE_MODULES_PYODIDE = 'node_modules/pyodide'
const LOCK_FILE_PATH = `${PYODIDE_DIR}/pyodide-lock.json`

async function loadPyodideInstance() {
  console.info('Setting up Pyodide + micropip')
  try {
    return await loadPyodide({ packageCacheDir: PYODIDE_DIR })
  } catch (err) {
    console.error('Failed to load Pyodide:', err)
    throw err
  }
}

async function getPyodideVersionFromPackageJson() {
  const packageJson = JSON.parse(await readFile('package.json'))
  return packageJson.dependencies.pyodide.replace('^', '')
}

async function getInstalledPyodideVersion() {
  try {
    const pyodidePackageJson = JSON.parse(await readFile(PYODIDE_PACKAGE_JSON))
    return pyodidePackageJson.version.replace('^', '')
  } catch {
    console.info('Pyodide package not found, proceeding with download.')
    return null
  }
}

async function ensureCorrectPyodideVersion() {
  const requiredVersion = await getPyodideVersionFromPackageJson()
  const installedVersion = await getInstalledPyodideVersion()

  if (installedVersion && installedVersion !== requiredVersion) {
    console.info('Pyodide version mismatch, removing public/pyodide directory')
    await rmdir(PYODIDE_DIR, { recursive: true })
  }
}

async function installPackages(pyodide) {
  console.info('Loading micropip package')
  await pyodide.loadPackage('micropip')
  const micropip = pyodide.pyimport('micropip')

  console.info('Downloading Pyodide packages:', PACKAGES)
  try {
    await Promise.all(PACKAGES.map(pkg => micropip.install(pkg)))
    console.info('All packages installed successfully')
  } catch (err) {
    console.error('Package installation failed:', err)
    throw err
  }
}

async function writeLockFile(micropip) {
  try {
    const lockFile = await micropip.freeze()
    await writeFile(LOCK_FILE_PATH, lockFile)
    console.info('Lock file written successfully')
  } catch (err) {
    console.error('Failed to write lock file:', err)
    throw err
  }
}

async function downloadPackages() {
  await ensureCorrectPyodideVersion()

  const pyodide = await loadPyodideInstance()
  await installPackages(pyodide)
  await writeLockFile(pyodide.pyimport('micropip'))
}

async function copyPyodideFiles() {
  console.info('Copying Pyodide files into static directory')
  try {
    const files = await readdir(NODE_MODULES_PYODIDE)
    await Promise.all(
      files.map(file => copyFile(`${NODE_MODULES_PYODIDE}/${file}`, `${PYODIDE_DIR}/${file}`)),
    )
    console.info('Pyodide files copied successfully')
  } catch (err) {
    console.error('Failed to copy Pyodide files:', err)
    throw err
  }
}

async function cleanupFiles() {
  await Promise.all([
    rm(`${PYODIDE_DIR}/console.html`),
    rm(`${PYODIDE_DIR}/README.md`),
  ])
}

async function main() {
  try {
    await downloadPackages()
    await copyPyodideFiles()
    await cleanupFiles()
    exit(0)
  } catch (err) {
    console.error('An error while downloading Pyodide:', err)
    exit(1)
  }
}

await main()
