import debugLog from '../../debugLog.js'
import { logWarning } from '../../serverlessLog.js'
import {
  supportedNodejs,
  supportedPython,
  supportedRuby,
  supportedJava,
  supportedGo,
} from '../../config/index.js'
import { satisfiesVersionRange } from '../../utils/index.js'

export default class HandlerRunner {
  #env = null
  #funOptions = null
  #options = null
  #runner = null

  constructor(funOptions, options, env, v3Utils) {
    this.#env = env
    this.#funOptions = funOptions
    this.#options = options
    if (v3Utils) {
      this.log = v3Utils.log
      this.progress = v3Utils.progress
      this.writeText = v3Utils.writeText
      this.v3Utils = v3Utils
    }
  }

  async _loadRunner() {
    const { useDocker, useChildProcesses, useWorkerThreads, allowCache } =
      this.#options

    const {
      functionKey,
      handlerName,
      handlerPath,
      handlerModuleNesting,
      runtime,
      timeout,
    } = this.#funOptions

    if (this.log) {
      this.log.debug(`Loading handler... (${handlerPath})`)
    } else {
      debugLog(`Loading handler... (${handlerPath})`)
    }

    if (useDocker) {
      // https://github.com/lambci/docker-lambda/issues/329
      if (runtime === 'nodejs14.x') {
        if (this.log) {
          this.log.warning(
            '"nodejs14.x" runtime is not supported with docker. See https://github.com/lambci/docker-lambda/issues/329',
          )
        } else {
          logWarning(
            '"nodejs14.x" runtime is not supported with docker. See https://github.com/lambci/docker-lambda/issues/329',
          )
        }
        throw new Error('Unsupported runtime')
      }

      if (runtime === 'python3.9') {
        if (this.log) {
          this.log.warning('"python3.9" runtime is not supported with docker.')
        } else {
          logWarning('"python3.9" runtime is not supported with docker.')
        }
        throw new Error('Unsupported runtime')
      }

      const dockerOptions = {
        host: this.#options.dockerHost,
        hostServicePath: this.#options.dockerHostServicePath,
        layersDir: this.#options.layersDir,
        network: this.#options.dockerNetwork,
        readOnly: this.#options.dockerReadOnly,
      }

      const { default: DockerRunner } = await import('./docker-runner/index.js')
      return new DockerRunner(
        this.#funOptions,
        this.#env,
        dockerOptions,
        this.v3Utils,
      )
    }

    if (supportedNodejs.has(runtime)) {
      if (useChildProcesses) {
        const { default: ChildProcessRunner } = await import(
          './child-process-runner/index.js'
        )
        return new ChildProcessRunner(
          this.#funOptions,
          this.#env,
          allowCache,
          this.v3Utils,
        )
      }

      if (useWorkerThreads) {
        // worker threads
        this._verifyWorkerThreadCompatibility()

        const { default: WorkerThreadRunner } = await import(
          './worker-thread-runner/index.js'
        )
        return new WorkerThreadRunner(this.#funOptions, this.#env, allowCache)
      }

      const { default: InProcessRunner } = await import(
        './in-process-runner/index.js'
      )
      return new InProcessRunner(
        functionKey,
        handlerPath,
        handlerName,
        handlerModuleNesting,
        this.#env,
        timeout,
        allowCache,
      )
    }

    if (supportedGo.has(runtime)) {
      const { default: GoRunner } = await import('./go-runner/index.js')
      return new GoRunner(this.#funOptions, this.#env, this.v3Utils)
    }

    if (supportedPython.has(runtime)) {
      const { default: PythonRunner } = await import('./python-runner/index.js')
      return new PythonRunner(
        this.#funOptions,
        this.#env,
        allowCache,
        this.v3Utils,
      )
    }

    if (supportedRuby.has(runtime)) {
      const { default: RubyRunner } = await import('./ruby-runner/index.js')
      return new RubyRunner(
        this.#funOptions,
        this.#env,
        allowCache,
        this.v3Utils,
      )
    }

    if (supportedJava.has(runtime)) {
      const { default: JavaRunner } = await import('./java-runner/index.js')
      return new JavaRunner(
        this.#funOptions,
        this.#env,
        allowCache,
        this.v3Utils,
      )
    }

    // TODO FIXME
    throw new Error('Unsupported runtime')
  }

  _verifyWorkerThreadCompatibility() {
    const { node: currentVersion } = process.versions
    const requiredVersionRange = '>=11.7.0'

    const versionIsSatisfied = satisfiesVersionRange(
      currentVersion,
      requiredVersionRange,
    )

    // we're happy
    if (!versionIsSatisfied) {
      if (this.log) {
        this.log.warning(
          `"worker threads" require node.js version ${requiredVersionRange}, but found version ${currentVersion}.
         To use this feature you have to update node.js to a later version.
        `,
        )
      } else {
        logWarning(
          `"worker threads" require node.js version ${requiredVersionRange}, but found version ${currentVersion}.
         To use this feature you have to update node.js to a later version.
        `,
        )
      }

      throw new Error(
        '"worker threads" are not supported with this node.js version',
      )
    }
  }

  // TEMP TODO FIXME
  isDockerRunner() {
    return this.#runner && this.#runner.constructor.name === 'DockerRunner'
  }

  // () => Promise<void>
  cleanup() {
    // TODO console.log('handler runner cleanup')
    return this.#runner.cleanup()
  }

  async run(event, context) {
    if (this.#runner == null) {
      this.#runner = await this._loadRunner()
    }

    return this.#runner.run(event, context)
  }
}

@@ -216,69 +216,8 @@
 .js'
-%0Aimport %7B satisfiesVersionRange %7D from '../../utils/index.js'
 %0A%0Aex
@@ -2760,83 +2760,8 @@
 ) %7B%0A
-        // worker threads%0A        this._verifyWorkerThreadCompatibility()%0A%0A
     
@@ -4211,16 +4211,16 @@
 O FIXME%0A
+
     thro
@@ -4263,935 +4263,8 @@
  %7D%0A%0A
-  _verifyWorkerThreadCompatibility() %7B%0A    const %7B node: currentVersion %7D = process.versions%0A    const requiredVersionRange = '%3E=11.7.0'%0A%0A    const versionIsSatisfied = satisfiesVersionRange(%0A      currentVersion,%0A      requiredVersionRange,%0A    )%0A%0A    // we're happy%0A    if (!versionIsSatisfied) %7B%0A      if (this.log) %7B%0A        this.log.warning(%0A          %60%22worker threads%22 require node.js version $%7BrequiredVersionRange%7D, but found version $%7BcurrentVersion%7D.%0A         To use this feature you have to update node.js to a later version.%0A        %60,%0A        )%0A      %7D else %7B%0A        logWarning(%0A          %60%22worker threads%22 require node.js version $%7BrequiredVersionRange%7D, but found version $%7BcurrentVersion%7D.%0A         To use this feature you have to update node.js to a later version.%0A        %60,%0A        )%0A      %7D%0A%0A      throw new Error(%0A        '%22worker threads%22 are not supported with this node.js version',%0A      )%0A    %7D%0A  %7D%0A%0A
   //

remove (now) useless worker thread support check (#1406)