import { resolve } from 'path'
import { readFileSync } from 'fs'
import {
  DeploymentPlatform,
  SupportedEnvironments,
  ContainerDeploymentRequest,
  DeploymentTemplate,
  ContainerDeploymentTemplate,
  SupportedDeploymentTypes,
  SupportedRegions
} from 'types'
import { CaddyClient } from './caddy-client'
import { DockerClient } from './docker-client'
import { DeploymentQueueClient } from './deployment-queue-client'
import { CiRestApi } from './ci-rest-api'
import { logInfo, logError } from 'log'

export class DeploymentPlatformClient {

  static start(pathToDeploymentPlatforms: string) {
    DeploymentPlatformClient.getDeploymentPlatforms(pathToDeploymentPlatforms).then(([firstPlatformFound]) => {

      if (!firstPlatformFound) {
        logError({  message: '[DeploymentPlatformClient] Could not find a deployment platform' })
        return Promise.reject()
      }

      CiRestApi.start(firstPlatformFound)
      CaddyClient.applyDefaultServerConfig(firstPlatformFound)

      DeploymentQueueClient.pollForDeploymentRequests(
        firstPlatformFound,
        (deploymentRequest: ContainerDeploymentRequest) => {
          DockerClient.deploy(firstPlatformFound, deploymentRequest)
          CaddyClient.deploy(firstPlatformFound, deploymentRequest)
          return Promise.resolve()
        }
      )

    })
    .catch(error => logError({  message: '[DeploymentPlatformClient] Could not start the deployment platform', error }))
  }

  //eslint-disable-next-line max-params
  static deploy(
    username,
    authentication,
    targetEnvironment,
    pathToDeploymentPlatforms,
    pathToOssyFile
  ) {
    return Promise.all([
      DeploymentPlatformClient.getDeploymentPlatforms(pathToDeploymentPlatforms),
      DeploymentPlatformClient.getDeploymentTemplates(pathToOssyFile)
    ])
      .then(([platforms, deploymentTemplates]) => {
        deploymentTemplates.map(deploymentTemplate => {

          logInfo({ message: `[DeploymentPlatformClient]: Found deployment platforms [${platforms.map(x => x.platformName).join(', ')}]` })

          const deploymentPlatform = platforms
            .map(platform => DeploymentPlatformClient.setCalculatedDeploymentPlatformValues({ ...platform, activeEnvironment: targetEnvironment }))
            .find(platform => platform.platformName === deploymentTemplate.targetDeploymentPlatform)

          if (!deploymentPlatform) {
            logError({ message: `[DeploymentPlatformClient] Could not find a deployment platform with the name ${deploymentTemplate.targetDeploymentPlatform}` })
            return Promise.reject()
          }

          process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'

          if (deploymentTemplate.type !== SupportedDeploymentTypes.Container) {
            logError({ message: `[DeploymentPlatformClient] Unsupported deployment type of ${deploymentTemplate.type}` })
            return Promise.reject()
          }

          const deploymentRequest: ContainerDeploymentRequest = {
            ...deploymentTemplate as ContainerDeploymentTemplate,
            env: DeploymentPlatformClient.getEnvironmentVariables(targetEnvironment, deploymentTemplate),
            username: username,
            authentication: authentication
          }

          return DeploymentQueueClient.sendDeploymentRequest(deploymentPlatform, deploymentRequest)

        })
      })
      .catch(error => logError({ message: '[DeploymentPlatformClient] Could not send deployment request', error }))
  }

  static getDeploymentTemplates(pathToOssyFile: string): Promise<DeploymentTemplate[]> {
    const ossyfile = JSON.parse(readFileSync(resolve(pathToOssyFile), 'utf8'))
    return Promise.resolve(ossyfile.deployments || [])
  }

  static getDeploymentPlatforms(pathToDeploymentPlatforms: string): Promise<DeploymentPlatform[]> {
    let deploymentPlatforms = JSON.parse(readFileSync(resolve(pathToDeploymentPlatforms), 'utf8'))

    if (!Array.isArray(deploymentPlatforms)) {
      deploymentPlatforms = [deploymentPlatforms]
    }

    return Promise.resolve((deploymentPlatforms || []).map(DeploymentPlatformClient.resolveDeploymentPlatformValues))
  }

  static getEnvironmentVariables(targetEnvironment: SupportedEnvironments, deploymentRequest: DeploymentTemplate) {
    const envs = deploymentRequest.env || {}
    return {
      ...(envs.shared || {}),
      ...(envs[targetEnvironment] || {})
    }
  }

  static resolveDeploymentPlatformValues(deploymentPlatform: DeploymentPlatform): DeploymentPlatform {
    return DeploymentPlatformClient.setCalculatedDeploymentPlatformValues(DeploymentPlatformClient.setDefaultDeploymentPlatformValues(deploymentPlatform))
  }

  static setDefaultDeploymentPlatformValues(deploymentPlatform: DeploymentPlatform): DeploymentPlatform {
    return {
      platformName: SupportedEnvironments.LOCAL,
      domain: 'localhost',
      activeEnvironment: SupportedEnvironments.LOCAL,
      supportedDeploymentTypes: ['CONTAINER'],
      ciSubDomain: 'ci',
      ciInternalServerPort: 3000,
      ciServerName: 'ci-client',
      ciDockerNetworkName: 'deployment-tools',
      awsRegion: SupportedRegions.North,
      // awsRoleToAssume: 'github-ci-role', //todo: find a way to have this a a default value and still keep the same logic when resolving aws credentials
      ...deploymentPlatform
    }
  }

  static setCalculatedDeploymentPlatformValues(deploymentPlatform: DeploymentPlatform): DeploymentPlatform {
    return {
      ...deploymentPlatform,
      awsDeploymentSqsArn: `https://sqs.${deploymentPlatform.awsRegion}.amazonaws.com/${deploymentPlatform.awsAccountId}/${deploymentPlatform.platformName}-${deploymentPlatform.activeEnvironment}`,
    }
  }

}
