import path from 'path'
import { promises as fs } from 'fs'
import wabtModule from 'wabt'
import ScriptLoaderClient from './ScriptLoaderClient'

interface Props {
  src: string,
  wasm?: string,
}

export interface ScriptFile {
  name: string,
  path: string,
}

const wabtModulePromise = wabtModule()

export default async function ScriptServer({ src, wasm, ...rest }: Props) {
  if (!src)
    throw new Error("ScriptLoader: No src parameter")

  const scriptPath = path.join(process.cwd(), 'scripts', src)
  const wasmPath = wasm && path.join(process.cwd(), 'scripts', wasm)

  const scriptContents = await fs.readFile(scriptPath)
  const wasmContents = wasmPath && await fs.readFile(wasmPath)

  let script: ScriptFile | undefined = undefined;

  if (scriptContents) {
    const scriptFileName = path.basename(src)
    const scriptDest = path.join(process.cwd(), '.next/static/scripts', src)
    const destDir = path.dirname(scriptDest)
    await fs.mkdir(destDir, { recursive: true })
    await fs.writeFile(scriptDest, scriptContents)
    script = {
      name: scriptFileName,
      path: path.join('/_next/static/scripts', src)
    }
  }

  let wasmCompiled: ScriptFile | undefined = undefined;
  if (wasmContents) {
    const wasmTextFileName = path.basename(wasm)
    const wasmBinFileName = wasmTextFileName.replace(/\.wat$/, '.wasm')
    const wabt = await wabtModulePromise

    const wasmParsed = wabt.parseWat(wasmTextFileName, wasmContents)
    const { buffer: wasmBinary } = wasmParsed.toBinary({ write_debug_names: true })
    const wasmDestText = path.join(process.cwd(), '.next/static/scripts', wasm)
    const wasmDestBinary = wasmDestText.replace(/\.wat$/, '.wasm')
    const destDir = path.dirname(wasmDestText)
    await fs.mkdir(destDir, { recursive: true })
    await fs.writeFile(wasmDestText, wasmContents)
    await fs.writeFile(wasmDestBinary, wasmBinary)

    wasmCompiled = {
      name: wasmBinFileName,
      path: path.join('/_next/static/scripts/', wasm.replace(/\.wat$/, '.wasm'))
    }
  }

  let processedArgs = {}
  for (const argName in rest) {
    const argValue = rest[argName]

    try {
      const argPath = path.join(process.cwd(), 'scripts', argValue)
      const argContents = await fs.readFile(argPath)
      const argFileName = path.basename(argValue)
      const argDest = path.join(process.cwd(), '.next/static/scripts', argValue)
      const argDir = path.dirname(argDest)
      await fs.mkdir(argDir, { recursive: true })
      await fs.writeFile(argDest, argContents)
      processedArgs[argName] = {
        name: argFileName,
        path: path.join('/_next/static/scripts', argValue)
      }
    } catch {
      processedArgs[argName] = argValue
    }
  }

  if (!script) return null

  return (
    <ScriptLoaderClient
      src={script.path}
      {...(wasmCompiled ? {
        wasmSrc: wasmCompiled.path
      } : {})}
      {...processedArgs}
    />
  )
}
