import hudson.tasks.test.AbstractTestResultAction
import hudson.model.Actionable
import ru.yandex.qatools.allure.jenkins.AllureReportBuildAction
import com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterDefinition

def slackNotify(String buildStatus , String channel, String message = 'undefined', blocks = []) {
  def slackResponse
  switch (buildStatus) {
    case 'SUCCESS':
      message = message != 'undefined' ? message : 'build is successful'
      slackResponse = slackSend(channel: "${channel}",
                                iconEmoji: ':rocket:',
                                color: "good",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}",
                                blocks: blocks)
      slackResponse.addReaction("thumbsup")
      break

    case 'UNSTABLE':
      message = message != 'undefined' ? message : 'build unstable'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':flying_saucer:',
                                color: "warning",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}",
                                blocks: blocks)
      slackResponse.addReaction("flying_saucer")
      break

    case 'FAILURE':
      message = message != 'undefined' ? message : 'build failed'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':fire_engine:',
                                color: "danger",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}",
                                blocks: blocks)
      slackResponse.addReaction("fire_engine")
      break

    case 'ABORTED':
      message = message != 'undefined' ? message : 'build aborted'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':umbrella_with_rain_dropsket:',
                                color: "danger",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}",
                                blocks: blocks)
      slackResponse.addReaction("umbrella_with_rain_drops")
      break

    // Custom statuses for Deployment notifications
    case 'DEPLOY_STARTED':
      message = message != 'undefined' ? message : 'deploy started'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':rocket:',
                                color: "warning",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}")
      slackResponse.addReaction("thumbsup")
      break

    case 'DEPLOY_FINISHED':
      message = message != 'undefined' ? message : 'deploy finished'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':tada:',
                                color: "warning",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}")
      slackResponse.addReaction("thumbsup")
      break

    case 'DEPLOY_FAILED':
      message = message != 'undefined' ? message : 'deploy failed'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':warning:',
                                color: "warning",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}")
      slackResponse.addReaction("fire_engine")
      break

    default:
      message = message != 'undefined' ? message : 'build result is not clear'
      slackResponse = slackSend(channel:  "${channel}",
                                iconEmoji: ':flying_saucer:',
                                color: "danger",
                                message: "<${env.BUILD_URL}|${env.JOB_NAME}> - ${message}")
      slackResponse.addReaction("flying_saucer")
      break
  }
  return slackResponse
}

def slackNotifyMainBranches(String buildStatus, String channel, String message = 'undefined') {
  switch(env.BRANCH_NAME) {
    case "master":
    case ~/release.*$/:
    case ~/hotfix.*$/:
    case ~/TAG_.*$/:
    case "develop":
      break
    default:
      return 0
  }
  slackResponse = slackNotify(buildStatus, channel, message)
  return slackResponse
}

def getSecretValueById(String secretName, String profileName) {
  return sh(script: """
    set -eu;
    aws secretsmanager get-secret-value \
      --profile ${profileName} \
      --secret-id '${secretName}' \
      --output json \
      | jq -r .SecretString
  """, returnStdout: true).trim()
}

def getBinarySecretValueById(String secretName, String fileName, String profileName) {
  sh(script: """
    set -eu;
    aws secretsmanager get-secret-value \
      --profile ${profileName} \
      --secret-id '${secretName}' \
      --output json \
      | jq -r .SecretBinary \
      | base64 -d > ${fileName}
  """)
}

def getSecretValueByIdAndJsonPath(String secretName, String profileName, String path) {
  return sh(script: """
    set -eu;
    aws secretsmanager get-secret-value \
      --profile ${profileName} \
      --secret-id '${secretName}' \
      --output json \
      | jq -r '.SecretString' \
      | jq -r '${path}'
  """, returnStdout: true).trim()
}

def loadEnvVarsFromSecrets (Map secrets, String secretsProfile) {
  secrets.each { key, value ->
    env[key] = getSecretValueById(value, secretsProfile)
  }
}

/**
 * Returns parameter value for a given parameter name using a given profile name
 * works for both String and SecureString parameters
 *
 * @param parameterName a name of AWS SSM parameter
 * @param profileName   a name of AWS profile
 * @return              a value of parameter
 */
def getParameterValueByName(String parameterName, String profileName) {
  return sh(script: """
    set -euo pipefail;
    aws ssm get-parameter \
      --profile ${profileName} \
      --with-decryption \
      --name '${parameterName}' \
      --output json | jq -r '.Parameter.Value'
  """, returnStdout: true).trim()
}

/**
 * Set parameter value for a given parameter name using a given profile name
 *
 * @param parameterName  a name of AWS SSM parameter
 * @param parameterValue a string to be stored in AWS SSM parameter
 * @param profileName    a name of AWS profile
 */
def putParameterValueByName(String parameterName, String parameterValue, String profileName) {
  echo sh(script: """
    set -euo pipefail;
    aws ssm put-parameter \
      --profile ${profileName} \
      --name '${parameterName}' \
      --value '${parameterValue}' \
      --overwrite
  """, returnStdout: true).trim()
}

/**
 * Returns parameter value for a given parameter name and path using a given profile name
 * works for both String and SecureString parameters
 *
 * @param parameterName a name of AWS SSM parameter
 * @param path          a json path
 * @param profileName   a name of AWS profile
 * @return              a value of parameter
 */
def getParameterValueByNameAndPath(String parameterName, String path, String profileName) {
  return sh(script: """
    set -euo pipefail;
    aws ssm get-parameter \
      --profile ${profileName} \
      --with-decryption \
      --name '${parameterName}' \
      --output json | jq -r '.Parameter.Value' | jq -r '${path}'
  """, returnStdout: true).trim()
}

def dataDogMetricPut(String metricName='', String value='', String timestamp='undefined', String DD_CLIENT_API_KEY='undefined', String host='jenkins.apollo.stream', String tags='[ "environment:Jenkins" ]' ) {
  if (timestamp == 'undefined'){
    timestamp = sh(returnStdout: true, script: "date +%s").trim()
  }
  if (DD_CLIENT_API_KEY == 'undefined'){
    DD_CLIENT_API_KEY=getSecretValueById("infra/jenkins/apollo/common/datadog/DEVELOPMENT_API_KEY", "gdb-delphi-dev")
  }
  script="""set +x; curl -s -X POST -H "Content-type: application/json" "https://api.datadoghq.com/api/v1/series?api_key=${DD_CLIENT_API_KEY}"\
  -d @- << EOF
{
  "series": [
    {
      "metric": "${metricName}",
      "points": [
        [
          "${timestamp}",
          "${value}"
        ]
      ],
      "type": "rate",
      "interval": 20,
      "host": "${host}",
      "tags": ${tags}
    }
  ]
}
EOF
"""
  ddResponse = sh(returnStdout: true, script: script).trim()
  return ddResponse
}

@NonCPS
def readXML(xml) {
    def DOM = new XmlParser().parseText(xml)
    return DOM
}

@NonCPS
def getBztReportURL(xml) {
    def ReportURL = new XmlParser().parseText(xml)['ReportURL'][0].text()
    return ReportURL
}

@NonCPS
def getBztReportGroupLabels(xml) {
    def GroupLabels = new XmlParser().parseText(xml).Group.collect{it.@label}
    return GroupLabels
}

@NonCPS
def getBztReportGroupMetricValue(xml,String groupLabel, String metricName) {
    String value=''
    def FinalStatus = new XmlParser().parseText(xml)
    FinalStatus['Group'].each { group ->
                if (group.@'label' == groupLabel){
                    value=value + group."${metricName}".@'value'[0]
                }
    }
    return value
}

@NonCPS
def getTestSummary() {
  def testResultAction = currentBuild.rawBuild.getAction(AbstractTestResultAction.class)
  def summary = ""

  if (testResultAction != null) {
    def total = testResultAction.getTotalCount()
    def failed = testResultAction.getFailCount()
    def skipped = testResultAction.getSkipCount()

    summary = "Test results:\n\t"
    summary = summary + ("Passed: " + (total - failed - skipped))
    summary = summary + (", Failed: " + failed)
    summary = summary + (", Skipped: " + skipped)
  } else {
    summary = "No tests found"
  }
  return summary
}

@NonCPS
def getAllureTestSummary(String build_url = "") { // set build_url to empty string for backward compatibility
  def rawBuild = currentBuild.rawBuild
  def testResultAction = rawBuild.getAction(AllureReportBuildAction)
  def parameters = rawBuild.getAction(ParametersAction)?.parameters

  if (testResultAction != null) {
    environment_info = []
    if (parameters != null) {
      parameters.each {
        if (it.getClass() != jenkins.plugins.parameter_separator.ParameterSeparatorValue) {
            if(it.name != 'STATE_FILE') { /* handle json content of STATE_FILE */
                environment_info.add("*${it.name}:* ${it.value}")
            }
        }
      }
    }

    test_results = []
    test_results.add("*Total:* ${testResultAction.getTotalCount()}")
    test_results.add("*Passed:* ${testResultAction.getPassedCount()}")
    test_results.add("*Failed:* ${testResultAction.getFailedCount()}")
    test_results.add("*Skipped:* ${testResultAction.getSkipCount()}")
    test_results.add("*Unknown:* ${testResultAction.getUnknownCount()}")
    test_results.add("*Broken:* ${testResultAction.getBrokenCount()}")

    return readJSON(text: """[
      {
        "type": "header",
        "text": {
          "type": "plain_text",
          "text": "Build parameters",
          "emoji": true
        }
      },
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "${environment_info.join('\\n')}"
        }
      },
      {
        "type": "divider"
      },
      {
        "type": "header",
        "text": {
          "type": "plain_text",
          "text": "Test results",
          "emoji": true
        }
      },
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "Build has been completed in branch: ${env.BRANCH_NAME}, please find the test results below:"
        }
      },
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "${test_results.join('\\n')}"
        },
        "accessory": {
          "type": "image",
          "image_url": "https://avatars.githubusercontent.com/u/5879127?s=100&v=4",
          "alt_text": "Allure Logo"
        }
      },
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "Link to the full report: <${env.BUILD_URL}/allure|Allure Report>"
        }
      },
      {
        "type": "divider"
      },
    ]""")
  } else {
    return readJSON(text: """[
      {
        "type": "section",
        "text" : {
          "type": "mrkdwn",
          "text": "No tests found"
        }
      }
    ]""")
  }
}

@NonCPS
def getGithubComment() {
  def triggerCause = currentBuild.rawBuild.getCause(org.jenkinsci.plugins.pipeline.github.trigger.IssueCommentCause)
  return (triggerCause) ? triggerCause.comment.trim().toLowerCase() : ""
}


def checkBox(String name, String values, String defaultValue,
             int visibleItemCnt=0, String description='', String delimiter=',') {

    // default same as number of values
    visibleItemCnt = visibleItemCnt ?: values.split(delimiter).size()
    return new ExtendedChoiceParameterDefinition(
      name, //name,
      "PT_CHECKBOX", //type
      values, //value
      "", //projectName
      "", //propertyFile
      "", //groovyScript
      "", //groovyScriptFile
      "", //bindings
      "", //groovyClasspath
      "", //propertyKey
      defaultValue, //defaultValue
      "", //defaultPropertyFile
      "", //defaultGroovyScript
      "", //defaultGroovyScriptFile
      "", //defaultBindings
      "", //defaultGroovyClasspath
      "", //defaultPropertyKey
      "", //descriptionPropertyValue
      "", //descriptionPropertyFile
      "", //descriptionGroovyScript
      "", //descriptionGroovyScriptFile
      "", //descriptionBindings
      "", //descriptionGroovyClasspath
      "", //descriptionPropertyKey
      "", //javascriptFile
      "", //javascript
      false, //saveJSONParameterToFile
      false, //quoteValue
      visibleItemCnt, //visibleItemCount
      description, //description
      delimiter //multiSelectDelimiter
    )
}

def getAccountIdByProfile(String profile) {
  switch (profile) {
    case "gdb-apollo-dev":
      return "725764004555"
    case "gdb-artistapp-dev":
      return "714609459574"
    case "gdb-core-dev":
      return "582412345909"
    case "gdb-databricks-dev":
      return "745243859288"
    case "gdb-datasci-dev":
      return "313114233826"
    case "gdb-delphi-dev":
      return "475275892927"
    case "gdb-infra-dev":
      return "483193324480"
    case "gdb-mct-dev":
      return "925412343888"
    case "gdb-prodhub-dev":
      return "703740486246"
    case "gdb-apollo-prod":
      return "801577982711"
    case "gdb-artistapp-prod":
      return "923763343190"
    case "gdb-core-prod":
      return "885200931856"
    case "gdb-databricks-prod":
      return "807702934854"
    case "gdb-delphi-prod":
      return "323555055331"
    case "gdb-infra-prod":
      return "890208661333"
    case "gdb-mct-prod":
      return "046770693006"
    default:
      return "000000000000"
  }
}

def paramsFromListOfMaps(inputParams) {
  outputParams = []
  for (input in inputParams) {
    switch (input.type) {
      case 'string':
        outputParams << string(name: input.name, defaultValue: input.default, description: input.description)
        break
      case 'booleanParam':
        outputParams << booleanParam(name: input.name, defaultValue: input.default, description: input.description)
        break
      case 'choice':
        outputParams << choice(name: input.name, choices: input.choices.collect(), description: input.description)
        break
      default:
        error 'unknown parameter type'
    }
  }
  return outputParams
}

def assumeDeploymentRole(String aws_acc_id, String session_name) {
  role_arn = "arn:aws:iam::${aws_acc_id}:role/cross_account_deployment_role"
  creds = sh(script: "aws sts assume-role --output json --profile default --role-arn ${role_arn} --role-session-name ${session_name}", , returnStdout: true).trim()

  object = readJSON text: creds, returnPojo: true
  env.AWS_ACCESS_KEY =  object.Credentials.AccessKeyId
  env.AWS_SECRET_KEY = object.Credentials.SecretAccessKey
  env.AWS_SESSION_TOKEN = object.Credentials.SessionToken

  echo("Generated creds\n${env.AWS_ACCESS_KEY}\n${env.AWS_SECRET_KEY}\n${env.AWS_SESSION_TOKEN}\n")
}
