import { exec } from 'child_process'
import fs from 'fs'
import { nanoid } from 'nanoid'
import path from 'path'
import { fileURLToPath } from 'url'
import { logInfo, logError } from './log'
import { PlatformConfig, ContainerDeploymentRequest } from 'types'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

export class DockerClient {

  static createDockerNetworkForContainerManagerServer(platformConfig: PlatformConfig) {
    logInfo({  message: '[DockerClient] Creating docker network for comunication between containers' })
    exec(`sudo docker network create ${platformConfig.ciDockerNetworkName}`)
  }

  static stopContainer(deploymentRequest: ContainerDeploymentRequest) {
    const name = deploymentRequest.image.replaceAll('/', '_')
    logInfo({  message: `Running docker stop for image with the name of ${name}` })
    return `sudo docker stop ${name} ||`
  }

  static startContainer(platformConfig: PlatformConfig, { image, containerPort, hostPort, registry, env }: ContainerDeploymentRequest) {
    const name = image.replaceAll('/', '_')
    const imageUrl = !!registry ? `${registry}/${image}` : image
    const envsAsString = Object.entries(env || {}).reduce((envs, [name, value]) => `${envs} --env ${name}=${value}`, '')
    logInfo({  message: `Running docker start for image with the name of ${name} with port mapping ${hostPort}:${containerPort} and source ${imageUrl}` })
    return `sudo docker run -d -p ${hostPort}:${containerPort} --name=${name} --network=${platformConfig.ciDockerNetworkName} --network-alias=${name} --rm ${envsAsString} ${imageUrl}`
  }

  static resolveCredentials({ registry, username, authentication }: ContainerDeploymentRequest) {
    const shouldAuthenticate = username || authentication

    if (!shouldAuthenticate) {
      logInfo({  message: 'No docker credentials provided, assuming image is publicly hosted' })
      return ''
    }

    logInfo({  message: `Resolving docker credentials for ${registry}` })
    return `sudo docker login ${registry} -u ${username} -p ${authentication}`
  }

  static deploy(platformConfig: PlatformConfig, deploymentRequest: ContainerDeploymentRequest): Promise<void> {
    return new Promise(resolve => {
      logInfo({  message: 'Starting docker deployment sequence' })

      const dockerCommandScript = `'#!/bin/bash'
${DockerClient.stopContainer(deploymentRequest)}
${DockerClient.resolveCredentials(deploymentRequest)}
${DockerClient.startContainer(platformConfig, deploymentRequest)}`

      const deploymentId = nanoid()

      const FilePaths = {
        DeploymentScript: `${__dirname}/${deploymentId}.sh`
      }

      fs.writeFileSync(FilePaths.DeploymentScript, dockerCommandScript)
      const command = exec(`bash ${FilePaths.DeploymentScript}`)

      command.stdout.on('data', data => {
        logInfo({  message: `[DockerClient]: ${data}` })
      })

      command.stderr.on('data', (data) => {
        logError({  message: `[DockerClient]: ${data}` })
      })

      command.on('close', code => {
        logInfo({  message: `[DockerClient] command exited with code ${code}` })
        fs.unlinkSync(FilePaths.DeploymentScript)
        resolve()
      })
    })

  }

}
