import groovy.json.JsonOutput

def packageUploadMode(mode) {
  switch(mode) {
    case "yes":
      return "OverwriteExisting"
    case "no":
      return "FailIfExists"
    case "ignore":
      return "IgnoreIfExists"
    default:
      return "FailIfExists"
  }
}

def zipPackageUploadMode(mode) {
  // new version of packageUploadMode, since we also have python packages.
  switch(mode) {
    case "error":
      return "FailIfExists"
    case "overwrite":
      return "OverwriteExisting"
    case "ignore":
      return "IgnoreIfExists"
    default:
      return "FailIfExists"
  }
}

def setDefaultDeployEnv(branch, projectGroup) {
  // Set dev env as a default environment for all branches, could be changed for GitFlow repos
  switch(branch) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return "skip_deployment"
    case "media_plan_master":
      return ("dev2" + " " + projectGroup)
    default:
      return ("dev" + " " + projectGroup)
  }
}

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

def setDefaultDeployDelphi(branch, projectGroup) {
  // Set dev env as a default environment for all branches, could be changed for GitFlow repos
  switch(branch) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return "skip_deployment"
    default:
      return ("qa" + " " + projectGroup)
  }
}

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

def setDefaultDeployEnvV2(branch, projectGroup) {
  switch(branch) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return ("stage" + " " + projectGroup)
    default:
      return ("dev" + " " + projectGroup)
  }
}

def setDefaultDeployEnvV3(branch, projectGroup) {
  switch(branch) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return ("prod" + " " + projectGroup)
    default:
      return ("dev" + " " + projectGroup)
  }
}

def setDefaultDeployEnvV4(branch, projectGroup, lower_environment_prefix="dev", upper_environment_prefix="prod") {
  switch(branch) {
    case "master":
    case "production":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return (upper_environment_prefix + " " + projectGroup)
    default:
      return (lower_environment_prefix + " " + projectGroup)
  }
}

def setDefaultDeployEnvMultiTenant(branch) {
  switch(branch) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return "stage"
    default:
      return "dev"
  }
}

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

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

def setDeploymentEnvV4(deployEnvChoice, projectGroup, branch, lower_environment_prefix="dev", upper_environment_prefix="prod") {
  switch(deployEnvChoice) {
    case "default":
      return setDefaultDeployEnvV4(branch, projectGroup, lower_environment_prefix, upper_environment_prefix)
    case "skip_deployment":
      return deployEnvChoice
    default:
      return (deployEnvChoice + " " + projectGroup)
  }
}

def setDefaultDeployEnvCRM(branch, projectGroup) {
  return ("dev" + " " + projectGroup)
}

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

def setDeploymentEnvMultiTenant(deployEnvChoice, branch) {
  if (deployEnvChoice == "default") return setDefaultDeployEnvMultiTenant(branch)
  return deployEnvChoice
}

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

def getBuildSuffix() {
  Date date = new Date()
  String dateFormatted = date.format('yyyyMMdd-HHmm-ssSSS')
  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.*$/:
    case ~/hotfix.*$/:
      return getCurrentVersion(workdir)
    default:
      return ((getCurrentVersion(workdir) =~ /(\d+.\d+.\d+)/)[0][0] + getBuildSuffix())
  }
}

// get version in case it is stored in versions.json file
def getCurrentVersionV2(target) {
  return (sh(script: "set -euxo pipefail; cat ${env.WORKSPACE}/versions.json | jq -r '.\"${target}\"'", returnStdout: true).trim())
}

def getVersionV2(branch,target) {
  switch(branch) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
      return getCurrentVersionV2(target)
    default:
      return ((getCurrentVersionV2(target) =~ /(\d+.\d+.\d+)/)[0][0] + getBuildSuffix())
  }
}

// set version for Maven release
def getVersionV3(branch,version) {
  switch(branch) {
    case "master":
    case ~/release.*$/:
      return version
    default:
      return version.replace("-SNAPSHOT",getBuildSuffix())
  }
}

// First version of function. File is moved to deployment folder, and the folder is packed as zip archive.
// Could be used for single files or folders
def createZipPackageV1(octopusProject, version, filename) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    mkdir -p deployment-${octopusProject}; mv ${filename} deployment-${octopusProject}/ ;
    dotnet octo pack \
      --id=${octopusProject} \
      --version=${version} \
      --format=zip \
      --basePath=./deployment-${octopusProject} ;
    rm -rf ./deployment-${octopusProject} ;
    """,
      returnStdout: true
    ).trim()
  return octo_output
}

def createZipPackageV3(octopusProject, version, folder) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo pack \
      --id=${octopusProject} \
      --version=${version} \
      --format=zip \
      --basePath=${folder}
    """,
      returnStdout: true
    ).trim()
  return octo_output
}

def pushPackage(packagePath, packageUploadMode) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo push \
      --package=${packagePath} \
      --overwrite-mode=${packageUploadMode}
    """,
      returnStdout: true
    ).trim()
  return octo_output
}

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

def createPackageBuildInformation(packageId, version) {
  String octopusBuildInfoFile=createOctopusBuildInformationJSON('octopus.buildinfo')
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo build-information \
      --package-id='${packageId}' \
      --file='${octopusBuildInfoFile}' \
      --overwrite-mode='OverwriteExisting' \
      --version ${version} \
  """,
      returnStdout: true
    ).trim()
  return octo_output
}

def getChangesWithTitleJSON() {
  // The function will provide of changelist for last commit,
  // or for whole PR if variable env.CHANGE_TARGET is presented
  def changeset = []
  if (env.CHANGE_TARGET) {
    // PR build
    // https://i.stack.imgur.com/4wMJI.png checkout to get difference between diff with .. and ...
    // Check PR-NN-head or PR-NN-merge
    switch(env.BRANCH_NAME) {
        case ~/PR-(\d+)-merge$/:
            PR_NUM=(env.BRANCH_NAME =~ /\d+/).findAll()[0]
            // echo "DEBUG: getChangesLastOrPR PR_NUM=${PR_NUM}"
            branchout_commit_id = sh(
              script: "git merge-base origin/${env.CHANGE_TARGET} origin/pr/${PR_NUM}",
              returnStdout: true).trim()
            // echo "DEBUG: getChangesLastOrPR branchout_commit_id=${branchout_commit_id}"
            changeset = sh(
              script: """git log  --no-merges --first-parent ${branchout_commit_id}..origin/${env.BRANCH_NAME} --pretty='format:{"Id":"%H","Comment":"%s"},'""",
              returnStdout: true).trim()
            break
        default:
            changeset = sh(
              script: """git log  --no-merges --first-parent origin/${env.CHANGE_TARGET}...origin/${env.BRANCH_NAME} --pretty='format:{"Id":"%H","Comment":"%s"},'""",
              returnStdout: true).trim()
    }
    //echo "DEBUG: getChangesLastOrPR changeset=${changeset}"
  }
  else {
    changeset = sh(
      script: """git log -1 --no-merges --first-parent --pretty='format:{"Id":"%H","Comment":"%s"},'""",
      returnStdout: true).trim()
  }
  // Get union of latest commit and PR changes into JSON array
  return """[ ${changeset} ]"""
}

def getChangesWithTitleJSON_v2() {
  // If latest commit was merge, get info from all commits from that merge
  // if not, get info from the latest commit
  def commit_ids = []
  def changesets = []

  parents = sh(
    script: "git log --format=%P -n 1 HEAD^..HEAD",
    returnStdout: true).trim()
  echo parents

  if ( parents.split(" ").size() > 1 )  {
    commit_ids = sh(
      script: """git log --no-merges \$(git merge-base --octopus \$(git log -1 --merges --pretty=format:%P)).. --boundary --pretty='format:%H'""",
      returnStdout: true).trim().split("\n")
  } else {
    commit_ids = sh(
      script: "git log -1 --pretty='format:%H'",
      returnStdout: true).trim().split("\n")
  }

  for (commit_id in commit_ids){
    def subject = sh(
      script: "git log --format=%s -n 1 ${commit_id}",
      returnStdout: true).trim()
    changesets.add([Id:commit_id, Comment:subject])
  }

  // Get union of latest commit and PR changes into JSON array
  return JsonOutput.toJson(changesets)
}

def createOctopusBuildInformationJSON(octopusBuildInformationFile) {
  String git_vcs_root = sh(script: """
    git config --get remote.origin.url
    """, returnStdout: true).trim()
  String git_lascommit_id = sh(script: """
    git rev-parse HEAD
    """, returnStdout: true).trim()
  String commitsJSON=getChangesWithTitleJSON_v2()
  String buildInformationJSON = """
{
   "BuildEnvironment":"Jenkins",
   "Branch":"${env.BRANCH_NAME}",
   "BuildNumber":"${env.BUILD_NUMBER}",
   "BuildUrl":"${env.BUILD_URL}",
   "VcsType":"Git",
   "VcsRoot":"${git_vcs_root}",
   "VcsCommitNumber":"${git_lascommit_id}",
   "Commits":${commitsJSON}
}"""
  writeFile(file: octopusBuildInformationFile, text: buildInformationJSON)
  return octopusBuildInformationFile
}

def prismaCloudScanImage(version,ecrRepository,awsProfile) {
  repositoryUri = sh(script: """
    set -euxo pipefail;
    aws ecr describe-repositories \
      --repository-name=${ecrRepository} \
      --profile ${awsProfile} \
    | jq -r '.repositories[0].repositoryUri'
  """, returnStdout: true).trim()
  //prismaCloudScanImage ca: '', cert: '', dockerAddress: 'unix:///var/run/docker.sock', image: "${repositoryUri}:${version}", key: '', logLevel: 'debug', podmanPath: '', project: '', resultsFile: 'prisma-cloud-scan-results.json', ignoreImageBuildTime: true
  //prismaCloudPublish resultsFilePattern: 'prisma-cloud-scan-results.json'
}

def releaseExists(octopusProject,version) {
  debugInfo = sh(script: "dotnet octo list-releases --project=${octopusProject} --outputFormat=json 2>&1", returnStdout: true ).trim()
  echo "Octo client output: ${debugInfo}"
  releaseIndex = sh(script: """
    set -euxo pipefail;
    dotnet octo list-releases --project=${octopusProject} --outputFormat=json \
    | jq '.[0].Releases? | map(.Version) | index("${version}")'
  """, returnStdout: true ).trim()
  return (releaseIndex!='null')
}

def createRelease(octopusProject, version, octopusChannel) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo create-release \
      --project ${octopusProject} \
      --version ${version} \
      --packageVersion ${version} \
      --channel '${octopusChannel}' \
  """,
      returnStdout: true
    ).trim()
  return octo_output
}

// For Maven - in this case package version and octopus release version differ
def createReleaseV2(octopusProject, version, packageVersion, octopusChannel) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo create-release \
      --project ${octopusProject} \
      --version ${version} \
      --packageVersion ${packageVersion} \
      --channel '${octopusChannel}' \
  """,
      returnStdout: true
    ).trim()
  return octo_output
}

def deployRelease(octopusProject, environment, version, octopusChannel) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo deploy-release \
      --waitfordeployment \
      --project ${octopusProject} \
      --deployto '${environment}'\
      --version ${version} \
      --channel '${octopusChannel}' \
      --deploymentTimeout '00:25:00'
  """,
      returnStdout: true
    ).trim()
  return octo_output
}

def deployReleaseToTenant(octopusProject, environment, version, octopusChannel, tenant) {
  String octo_output = sh(script: """
    set -euxo pipefail;
    dotnet octo deploy-release \
      --waitfordeployment \
      --tenant '${tenant}' \
      --project ${octopusProject} \
      --deployto '${environment}'\
      --version ${version} \
      --channel '${octopusChannel}' \
      --deploymentTimeout '00:25:00'
  """,
    returnStdout: true
  ).trim()
  return octo_output
}

def deployReleaseToAllTenants(octopusProject, environment, version, octopusChannel) {
  deployReleaseToTenant(octopusProject, environment, version, octopusChannel, "*")
}

def checkVersionAndPushToECR(imageUploadMode, version, ecrRepo, awsProfile, workDir, pushCommand='make docker/push') {
  if (imageUploadMode == 'error' && ecrImageTagExists(version,ecrRepo,awsProfile)) {
    error "Image with ${env.VERSION} version tag already exists. To ignore this error set IMAGE_UPLOAD_MODE parameter to overwrite or skip_build"
  }
  if (imageUploadMode == 'overwrite' || !ecrImageTagExists(version,ecrRepo,awsProfile)) {
    // Run PrismaCloud Scan before upload
    // disabled due to java heap space issue 2022-08-23
    //prismaCloudScanImage(version,ecrRepo,awsProfile)
    createPackageBuildInformation(ecrRepo, version)
    sh(script: "cd ${workDir} && ${pushCommand}",returnStdout: true).trim()
  }
}
