def setDefaultDeployEnv(branch) {
  // Set dev env as a default environment for all branches, could be changed for GitFlow repos
  switch(branch) {
    case "master":
      return "dev"
    default:
      return "dev"
  }
}

def setDeploymentEnv(deployEnvChoice, projectGroup, branch) {
  switch(deployEnvChoice) {
    case "default":
      return (setDefaultDeployEnv(branch) + " " + projectGroup)
    case "skip_deployment":
      return deployEnvChoice
    default:
      return (deployEnvChoice + " " + projectGroup)
  }
}

def setOctoChannel(branch) {
  // Set Octopus release channel based on branch name
  switch(branch) {
    case "master":
    case "release":
      return "release"
    default:
      return "develop"
  }
}

def getBuildSuffix() {
  Date date = new Date()
  String dateFormatted = date.format("yyyyMMddHHmm")
  String gitCommitHash = sh(returnStdout: true, script: 'git rev-parse --short=8 HEAD').trim()
  return ("-build" + dateFormatted + "." + gitCommitHash)
}

def getCurrentVersion(workdir) {
  return (sh(script: "set -euxo pipefail; cat ${workdir}/VERSION", returnStdout: true).trim())
}

def getVersion(branch,workdir) {
  switch(branch) {
    case "master":
    case "release":
      return getCurrentVersion(workdir)
    default:
      return (getCurrentVersion(workdir) + getBuildSuffix())
  }
}

def ecrImageTagExists(version,ecrRepository,awsProfile) {
  tagIndex = sh(script: """
    set -euxo pipefail;
    aws ecr list-images \
      --repository-name=${ecrRepository} \
      --profile ${awsProfile} \
    | jq --arg VERSION "${version}" '.imageIds | map(.imageTag) | index(\$VERSION)'
  """, returnStdout: true).trim()
  return (tagIndex!='null')
}


def octopusReleaseExists(octopusProject,version) {
  String releaseIndex = sh(script: """
      set -euxo pipefail;
      dotnet octo list-releases --project=${octopusProject} --outputFormat=json \
      | jq --arg VERSION "${version}" '.[0].Releases? | map(.Version) | index(\$VERSION)'
      """,
      returnStdout: true
  ).trim()
  return (releaseIndex!='null')
}

def createOctopusRelease(octopusProject, version, octopusChannel) {
  String octo_output = sh(script: """
    dotnet octo create-release \
      --project ${env.OCTOPUS_PROJECT} \
      --version ${env.VERSION} \
      --packageVersion ${VERSION} \
      --channel '${env.OCTOPUS_CHANNEL}' \
  """,
      returnStdout: true
    ).trim()
  return octo_output
}

def deployOctopusRelease(octopusProject, environment, version, octopusChannel) {
  String octo_output = sh(script: """
    dotnet octo deploy-release \
      --waitfordeployment \
      --project ${octopusProject} \
      --deployto '${environment}'\
      --version ${version} \
      --channel '${octopusChannel}' \
  """,
      returnStdout: true
    ).trim()
  return octo_output
}

pipeline {
  agent {
    label 'linux-agents'
  }
  options {
    disableConcurrentBuilds()
    ansiColor('xterm')
  }
  environment {
    // Using fake branch name to reuse usual functions
    FAKE_BRANCH_NAME = "${params.ENVIRONMENT == 'prod' ? 'master' : 'develop'}"
    ENV_PREFIX       = "${params.ENVIRONMENT == 'prod' ? 'prod' : 'dev'}"
    STAGE            = "${env.ENV_PREFIX}"
    AWS_PROFILE      = "${params.ENVIRONMENT == 'prod' ? 'sme-prod' : 'sme-dev'}"
    ECR              = "475275892927.dkr.ecr.us-east-1.amazonaws.com"

    DELPHI_DEV_DEPLOY   = 'delphi/dev/deploy'
    DELPHI_STAGE_DEPLOY = 'delphi/stage/deploy'
    DELPHI_QA_DEPLOY    = 'delphi/qa/deploy'
    DELPHI_PROD_DEPLOY  = 'delphi/prod/deploy'
    TF_IN_AUTOMATION    = 1

    // Octopus parameters
    PROJECT_GROUP       = 'infra'
    OCTOPUS_CLI_SERVER  = "https://octopus.delphi.zone"
    OCTOPUS_CLI_API_KEY = sh(script: "aws secretsmanager get-secret-value --profile sme-dev --secret-id 'infra/jenkins/octopus_api_key' --output json | jq .SecretString | tr -d '\"'", returnStdout: true).trim()
    OCTOPUS_CHANNEL     = setOctoChannel(env.BRANCH_NAME)
    DEPLOY_ENV          = setDeploymentEnv(params.DEPLOY_ENV_CHOICE, env.PROJECT_GROUP, env.BRANCH_NAME )
  }
  parameters {
    choice(
      choices: [
        'Please make a choice',
        'user_access_revoker',
        'secrets_rotator',
        'dynamodb_s3_exporter',
        'python-testing',
        'terraform/delphi/dev/deploy',
        'terraform/delphi/qa/deploy',
        'terraform/delphi/stage/deploy',
        'terraform/delphi/prod/deploy'
      ],
      description: 'Force build all or some modules',
      name: 'FORCE_BUILD'
    )
    choice(
      choices: [
        'dev',
        'qa',
        'stage',
        'uat',
        'prod'
      ],
      description: 'Name of the environment of the upstream job.',
      name: 'ENVIRONMENT',
    )
    choice(
      choices: [
        'default',
        'dev',
        'skip_deployment'
      ],
      description: 'Set environment for deployment. Development by default',
      name: 'DEPLOY_ENV_CHOICE'
    )
    choice(
      choices: [
        'error',
        'overwrite',
        'skip_build'
      ],
      description: "Docker image upload mode - ovewrite if the image with such tag already exists, raise error if exists, or skip build part if exists.",
      name: 'IMAGE_UPLOAD_MODE'
    )
    choice(
      choices: [
        'no',
        'yes'
      ],
      description: "Don't raise errors in case release already exists",
      name: 'SKIP_RELEASE_CREATION'
    )
  }
  stages {
    stage('user_access_revoker') {
      when {
        anyOf {
          allOf {
            changeset "**/Lambda/user_access_revoker/**"
            expression { return params.FORCE_BUILD == 'Please make a choice' }
          }
          expression { return params.FORCE_BUILD == 'user_access_revoker' }
        }
      }
      environment {
        AWS_PROFILE       = "sme-dev"
        PROJECT_NAME      = "user_access_revoker"
        OCTOPUS_PROJECT   = "${env.PROJECT_GROUP}.${env.PROJECT_NAME.replace('_','-')}"
        WORK_DIR          = "${env.WORKSPACE}/Lambda/${env.PROJECT_NAME}"
        ECR_REPOSITORY    = "security/${env.PROJECT_NAME.replace('_','-')}"
        REGISTRY          = "${env.ECR}/${env.ECR_REPOSITORY}"
        VERSION           = getVersion(env.BRANCH_NAME,env.WORK_DIR)
      }
      stages {
        stage('Check version') {
          when { expression { return params.IMAGE_UPLOAD_MODE == 'error' && ecrImageTagExists(env.VERSION,env.ECR_REPOSITORY,env.AWS_PROFILE) }}
          steps { error "Image with ${env.VERSION} version tag already exists. To ignore this error set IMAGE_UPLOAD_MODE parameter to overwrite or skip_build"}
        }
        stage('Build and push to ECR') {
          when {
            expression { return params.IMAGE_UPLOAD_MODE == 'overwrite' || !ecrImageTagExists(env.VERSION,env.ECR_REPOSITORY,env.AWS_PROFILE) }
          }
          steps {
            sh "cd ${WORK_DIR} && make docker/build"
            sh "cd ${WORK_DIR} && make docker/push"
          }
        }
        stage('Create a release in octopus') {
          //Check if release already exists. If it does, raise error or skip it, based on given param
          when {
            expression { return params.SKIP_RELEASE_CREATION == 'no' || !octopusReleaseExists(env.OCTOPUS_PROJECT,env.VERSION) }
          }
          steps {
            echo createOctopusRelease(env.OCTOPUS_PROJECT,env.VERSION,env.OCTOPUS_CHANNEL)
          }
        }
        stage('Deploy release in octopus') {
          when {
            expression { return env.DEPLOY_ENV != 'skip_deployment' }
          }
          steps {
            echo deployOctopusRelease(env.OCTOPUS_PROJECT,env.DEPLOY_ENV,env.VERSION,env.OCTOPUS_CHANNEL)
          }
        }
      }
    }
    stage('secrets_rotator') {
      when {
        anyOf {
          allOf {
            changeset "**/Lambda/secrets_rotator/**"
            expression { return params.FORCE_BUILD == 'Please make a choice' }
          }
          expression { return params.FORCE_BUILD == 'secrets_rotator' }
        }
      }
      environment {
        AWS_PROFILE       = "gdb-infra-dev"
        PROJECT_NAME      = "secrets_rotator"
        OCTOPUS_PROJECT   = "${env.PROJECT_GROUP}.${env.PROJECT_NAME.replace('_','-')}"
        WORK_DIR          = "${env.WORKSPACE}/Lambda/${env.PROJECT_NAME}"
        ECR_REPOSITORY    = "infra/lambda/${env.PROJECT_NAME.replace('_','-')}"
        ECR               = "483193324480.dkr.ecr.us-east-1.amazonaws.com"
        REGISTRY          = "${env.ECR}/${env.ECR_REPOSITORY}"
        VERSION           = getVersion(env.BRANCH_NAME,env.WORK_DIR)
      }
      stages {
        stage('Check version') {
          when { expression { return params.IMAGE_UPLOAD_MODE == 'error' && ecrImageTagExists(env.VERSION,env.ECR_REPOSITORY,env.AWS_PROFILE) }}
          steps { error "Image with ${env.VERSION} version tag already exists. To ignore this error set IMAGE_UPLOAD_MODE parameter to overwrite or skip_build"}
        }
        stage('Build and push to ECR') {
          when {
            expression { return params.IMAGE_UPLOAD_MODE == 'overwrite' || !ecrImageTagExists(env.VERSION,env.ECR_REPOSITORY,env.AWS_PROFILE) }
          }
          steps {
            sh "cd ${WORK_DIR} && make docker/build"
            sh "cd ${WORK_DIR} && make docker/push"
          }
        }
        stage('Create a release in octopus') {
          //Check if release already exists. If it does, raise error or skip it, based on given param
          when {
            expression { return params.SKIP_RELEASE_CREATION == 'no' || !octopusReleaseExists(env.OCTOPUS_PROJECT,env.VERSION) }
          }
          steps {
            echo createOctopusRelease(env.OCTOPUS_PROJECT,env.VERSION,env.OCTOPUS_CHANNEL)
          }
        }
      }
    }
    stage('dynamodb_s3_exporter') {
      when {
        anyOf {
          changeset "**/Docker/dynamodb_s3_exporter/**"
          expression {
            return params.FORCE_BUILD == 'dynamodb_s3_exporter'
          }
        }
      }
      environment {
        WORK_DIR = "${WORKSPACE}/Docker/dynamodb_s3_exporter"
        VERSION  = "${env.BRANCH_NAME == 'master' ? 'master':'develop'}"
      }
      stages {
        stage('Build and upload') {
          steps {
            sh "cd ${WORK_DIR} && make docker/build"
            sh "cd ${WORK_DIR} && make docker/push"
          }
        }
      }
    }
    stage('delphi/dev/deploy') {
      when {
        expression {
          return params.FORCE_BUILD == "terraform/${DELPHI_DEV_DEPLOY}"
        }
      }
      environment {
        WORK_DIR = "${WORKSPACE}/Terraform/${DELPHI_DEV_DEPLOY}/"
      }
      stages {
        stage('Validate') {
          steps {
            sh "set +x; aws secretsmanager get-secret-value --profile sme-dev --profile sme-dev --secret-id 'infra/jenkins/ssh/dna-ci-ro' --output json | jq .SecretString | xargs echo -e > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa"
            lock('init_tf_plugins') {
              sh "cd ${WORK_DIR} && terraform get -update"
              sh "cd ${WORK_DIR} && terraform init -input=false"
            }
            sh "cd ${WORK_DIR} && terraform validate"
            sh "cd ${WORK_DIR} && terraform fmt -check=true -write=false -diff=true || exit 0"
          }
        }
        stage('Plan') {
          steps {
            sh "cd ${WORK_DIR} && terraform plan -out out.tfplan -input=false -target module.slz_state_machine"
          }
        }
        stage('Apply') {
          steps {
            echo "Applying changes"
            sh "cd ${WORK_DIR} && terraform apply -auto-approve -input=false out.tfplan"
          }
        }
      }
    }
    stage('delphi/qa/deploy') {
      when {
        expression {
          return params.FORCE_BUILD == "terraform/${DELPHI_QA_DEPLOY}"
        }
      }
      environment {
        WORK_DIR = "${WORKSPACE}/Terraform/${DELPHI_QA_DEPLOY}/"
      }
      stages {
        stage('Validate') {
          steps {
            sh "set +x; aws secretsmanager get-secret-value --profile sme-dev --secret-id 'infra/jenkins/ssh/dna-ci-ro' --output json | jq .SecretString | xargs echo -e > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa"
            lock('init_tf_plugins') {
              sh "cd ${WORK_DIR} && terraform get -update"
              sh "cd ${WORK_DIR} && terraform init -input=false"
            }
            sh "cd ${WORK_DIR} && terraform validate"
            sh "cd ${WORK_DIR} && terraform fmt -check=true -write=false -diff=true || exit 0"
          }
        }
        stage('Plan') {
          steps {
            sh "cd ${WORK_DIR} && terraform plan -out out.tfplan -input=false -target module.slz_state_machine"
          }
        }
        stage('Apply') {
          steps {
            echo "Applying changes"
            sh "cd ${WORK_DIR} && terraform apply -auto-approve -input=false out.tfplan"
          }
        }
      }
    }
    stage('delphi/stage/deploy') {
      when {
        expression {
          return params.FORCE_BUILD == "terraform/${DELPHI_STAGE_DEPLOY}"
        }
      }
      environment {
        WORK_DIR = "${WORKSPACE}/Terraform/${DELPHI_STAGE_DEPLOY}/"
      }
      stages {
        stage('Validate') {
          steps {
            sh "set +x; aws secretsmanager get-secret-value --profile sme-dev --secret-id 'infra/jenkins/ssh/dna-ci-ro' --output json | jq .SecretString | xargs echo -e > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa"
            lock('init_tf_plugins') {
              sh "cd ${WORK_DIR} && terraform get -update"
              sh "cd ${WORK_DIR} && terraform init -input=false"
            }
            sh "cd ${WORK_DIR} && terraform validate"
            sh "cd ${WORK_DIR} && terraform fmt -check=true -write=false -diff=true || exit 0"
          }
        }
        stage('Plan') {
          steps {
            sh "cd ${WORK_DIR} && terraform plan -out out.tfplan -input=false -target module.slz_state_machine"
          }
        }
        stage('Apply') {
          steps {
            echo "Applying changes"
            sh "cd ${WORK_DIR} && terraform apply -auto-approve -input=false out.tfplan"
          }
        }
      }
    }
    stage('delphi/prod/deploy') {
      when {
        expression {
          return params.FORCE_BUILD == "terraform/${DELPHI_PROD_DEPLOY}"
        }
      }
      environment {
        WORK_DIR = "${WORKSPACE}/Terraform/${DELPHI_PROD_DEPLOY}/"
      }
      stages {
        stage('Validate') {
          steps {
            sh "set +x; aws secretsmanager get-secret-value --profile sme-dev --secret-id 'infra/jenkins/ssh/dna-ci-ro' --output json | jq .SecretString | xargs echo -e > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa"
            lock('init_tf_plugins') {
              sh "cd ${WORK_DIR} && terraform get -update"
              sh "cd ${WORK_DIR} && terraform init -input=false"
            }
            sh "cd ${WORK_DIR} && terraform validate"
            sh "cd ${WORK_DIR} && terraform fmt -check=true -write=false -diff=true || exit 0"
          }
        }
        stage('Plan') {
          steps {
            sh "cd ${WORK_DIR} && terraform plan -out out.tfplan -input=false -target module.slz_state_machine"
          }
        }
        stage('Apply') {
          steps {
            echo "Applying changes"
            sh "cd ${WORK_DIR} && terraform apply -auto-approve -input=false out.tfplan"
          }
        }
      }
    }
    stage('sbt') {
      when {
       anyOf {
          allOf {
           changeset "**/Docker/sbt/**"
            expression {
             return params.FORCE_BUILD == 'Please make a choice'
           }
          }
         expression {
           return params.FORCE_BUILD == 'sbt'
         }
       }
     }

      environment {
        WORK_DIR = "${WORKSPACE}/Docker/sbt"
      }
      stages {
        stage('Build and push') {
          steps {
            sh "cd ${WORK_DIR} && make docker/build"
            sh "cd ${WORK_DIR} && make docker/push"
          }
        }
      }
    }
    stage('python-testing') {
      when {
       anyOf {
          allOf {
           changeset "**/Docker/python-testing/**"
            expression {
             return params.FORCE_BUILD == 'Please make a choice'
           }
          }
         expression {
           return params.FORCE_BUILD == 'python-testing'
         }
       }
     }

      environment {
        WORK_DIR = "${WORKSPACE}/Docker/python-testing"
        REPOSITORY = "python-testing"
        VERSION  = "3.7-alpine"
      }
      stages {
        stage('Build and upload') {
          steps {
            echo "Using tag ${env.ECR}/${env.REPOSITORY}:${env.VERSION}"
            sh "cd ${WORK_DIR} && make docker/build"
            sh "cd ${WORK_DIR} && make docker/push"
          }
        }
      }
    }
  }
  /*post {
    cleanup {
      deleteDir()
    }
  }*/
}
