string GITHUB_REPOSITORY = 'playwright-tests'
string ECR_ACCOUNT_ID = '086679231553'
string QA_ACCOUNT_ID = '437795906767'
String SESSION_NAME = 'qa-playwright-tests-job'
List<String> AWS_REGIONS = ['us-east-1']
String QA_DEPLOYMENT_ROLE = 'prod-jenkins-aws-pipeline-agent'
String PROD_ACCOUNT_ID = '437795906767'
String PROD_DEPLOYMENT_ROLE = 'prod-jenkins-aws-pipeline-agent'
def qa_task_details
// Tag of the base images (lambda-test-runner-base, playwright-tests-base) the
// runner builds pull from ECR. Safe default 'latest' (last prod-promoted
// bases); overridden to the commit SHA by the Build Base Images stage when
// base inputs change.
def baseTag = 'latest'
def isGithubQaComment = env.GITHUB_COMMENT && env.GITHUB_COMMENT.toLowerCase().contains('jenkins deploy to qa')
def isGithubQaDeployAndRun = env.GITHUB_COMMENT && env.GITHUB_COMMENT.toLowerCase().startsWith('jenkins deploy to qa and run @')
def extractParam = { String paramName, String defaultValue = '' ->
    isGithubQaDeployAndRun && (env.GITHUB_COMMENT =~ /${paramName}=(\S+)/) ? (env.GITHUB_COMMENT =~ /${paramName}=(\S+)/)[0][1] : defaultValue
}
def extractedTags = isGithubQaDeployAndRun ? (env.GITHUB_COMMENT =~ /(?i)jenkins deploy to qa and run (.+?)(?:\s+CONFIG_ENV=|\s+RUNTIME_ENV=|\s+ADDITIONAL_ENV_VARS=|\s*$)/)[0][1].trim() : ''
def extractedConfigEnv = extractParam('CONFIG_ENV', 'qa')
def extractedRuntimeEnv = extractParam('RUNTIME_ENV', 'lambda')
def extractedAdditionalEnvVars = extractParam('ADDITIONAL_ENV_VARS')


pipeline {
    agent any

    options {
        ansiColor('xterm')
        disableConcurrentBuilds()
        timestamps()
    }

    parameters {
        string(
            name: 'SHARED_LIBRARIES_VERSION',
            defaultValue: 'master',
            description: 'The version of the Jenkins shared libraries to use. Can be a branch, tag or Git revision.'
        )
        booleanParam(
            name: 'RUN_TESTS',
            defaultValue: false,
            description: 'True to run tests, false to deploy Docker container.'
        )
        string(
            name: 'TAGS',
            defaultValue: '',
            description: 'Test tags to run when RUN_TESTS is true.'
        )
        choice(
        name: 'RUNTIME_ENV',
        choices: ['lambda', 'ecs'],
        description: 'The runtime environment to use for the tests. Default is lambda, can be set to ecs.'
        )
        choice(
        name: 'CONFIG_ENV',
        choices: ['qa', 'prod'],
        description: 'The environment to use for the application under test. Default is qa, can be set to prod'
        )
        choice(
        name: 'FRAMEWORK_ENV',
        choices: ['prod', 'qa'],
        description: 'The environment to use for the framework. Default is prod, can be set to qa if you want to test changes only deployed to qa framework'
        )
        string(
        name: 'FRAMEWORK_RUNNER_TAG',
        defaultValue: 'latest',
        description: 'Tag for the framework runner container to use. Default is latest, can be set to a git commit if needed.'
        )
        string(
        name: 'ADDITIONAL_ENV_VARS',
        defaultValue: '',
        description: 'Additional environment variables to pass to the test runner as a JSON array. E.g. [{"TRACE":"on"}] to capture Playwright traces for all test attempts including the first run.'
        )
    }

    triggers {
        issueCommentTrigger('.*deploy to qa.*|.*jenkins retest.*')
    }

    stages {
        stage('Set Build Name') {
          steps {
            script {
              currentBuild.displayName = "#${BUILD_NUMBER} - ${params.TAGS}"
            }
          }
        }

        stage('Load Shared Libraries') {
            steps {
                library "jenkins-global-libraries@${params.SHARED_LIBRARIES_VERSION}"
            }
        }

        stage('Unit Tests and Lint') {
            when {
                expression {
                    !params.RUN_TESTS && !isGithubQaDeployAndRun
                }
            }
            steps {
                cleanWs()
                checkout scm
                sh 'make clean_reports'
                sh 'rm -rf playwright-tests'
                pnpmRun(scriptNames: ['lint:check', 'format:check', 'check:id-tags', 'test'])
            }
            post {
                success {
                    githubPostCommitStatus(
                        repo: GITHUB_REPOSITORY,
                        state: 'success',
                        description: 'Lint and unit tests passed',
                        context: 'ci/lint-and-unit'
                    )
                }
                failure {
                    githubPostCommitStatus(
                        repo: GITHUB_REPOSITORY,
                        state: 'failure',
                        description: 'Lint or unit tests failed',
                        context: 'ci/lint-and-unit'
                    )
                }
            }
        }

        stage('Validate Software Catalog Definition') {
            when {
                expression {
                    !isGithubQaComment && !params.RUN_TESTS
                }
            }
            steps {
                datadogSoftwareCatalogValidate()
            }
        }

        // Must run before the parallel ECR stage: the runner builds do
        // FROM base:${BASE_TAG} with --pull, so the base tags have to exist
        // in ECR before those builds start.
        stage('Build Base Images') {
            when {
                expression {
                    (env.BRANCH_NAME == 'master' || isGithubQaComment) && !params.RUN_TESTS &&
                    getModifiedPaths(paths: ['pnpm-lock.yaml', 'pnpm-workspace.yaml', 'package.json', 'Dockerfile'])
                }
            }
            steps {
                script {
                    baseTag = env.GIT_COMMIT
                    def baseBuilds = [:]
                    if (env.BRANCH_NAME == 'master' || extractedRuntimeEnv != 'ecs') {
                        baseBuilds['lambda-base'] = {
                            dockerToEcr(
                                awsRegions: AWS_REGIONS,
                                ecrAccountId: ECR_ACCOUNT_ID,
                                imageName: 'lambda-test-runner-base',
                                dockerBuildTarget: 'lambda-test-runner-base',
                                imageTag: env.GIT_COMMIT,
                                pushLatest: false
                            )
                        }
                    }
                    if (env.BRANCH_NAME == 'master' || extractedRuntimeEnv == 'ecs') {
                        baseBuilds['ecs-base'] = {
                            dockerToEcr(
                                awsRegions: AWS_REGIONS,
                                ecrAccountId: ECR_ACCOUNT_ID,
                                imageName: 'playwright-tests-base',
                                dockerBuildTarget: 'ecs-runner-base',
                                imageTag: env.GIT_COMMIT,
                                pushLatest: false
                            )
                        }
                    }
                    parallel baseBuilds
                }
            }
        }
        stage('Deploy Docker Containers to ECR') {
            when {
                expression {
                    (env.BRANCH_NAME == 'master' || isGithubQaComment) && !params.RUN_TESTS
                }
            }
            failFast true
            parallel {
                stage('Build and Push ecs-runner Docker Image') {
                    when {
                        expression {
                            env.BRANCH_NAME == 'master' || extractedRuntimeEnv == 'ecs'
                        }
                    }
                    steps {
                        dockerToEcr(
                            awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            imageName: 'playwright-tests',
                            dockerBuildTarget: 'ecs-runner',
                            dockerBuildArgs: [BASE_TAG: baseTag],
                            imageTag: env.GIT_COMMIT
                        )
                    }
                }
                stage('Build and Push framework-runner Docker Image') {
                    steps {
                        dockerToEcr(
                            awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            imageName: 'playwright-framework-runner',
                            dockerBuildTarget: 'framework-runner',
                            imageTag: env.GIT_COMMIT,
                            pushLatest: false
                        )
                    }
                }
                stage('Build and Push lambda-test-runner Docker Image') {
                    when {
                        expression {
                            env.BRANCH_NAME == 'master' || extractedRuntimeEnv != 'ecs'
                        }
                    }
                    steps {
                        dockerToEcr(
                            awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            imageName: 'lambda-playwright-test-runner',
                            dockerBuildTarget: 'lambda-test-runner',
                            dockerBuildArgs: [BASE_TAG: baseTag],
                            imageTag: env.GIT_COMMIT,
                            pushLatest: false
                        )
                    }
                }
            }
        }
        stage('Deploy container to QA Lambda') {
            when {
                expression {
                    (env.BRANCH_NAME == 'master' || (isGithubQaComment && extractedRuntimeEnv != 'ecs')) && !params.RUN_TESTS
                }
            }
            steps {
                script {
                    lambdaDeploy environment: 'qa',
                                awsRegions: AWS_REGIONS,
                                imageTag: env.GIT_COMMIT,
                                imageName: "lambda-playwright-test-runner",
                                functionName: "qa-playwright-tests",
                                ecrRegistryAccountId: ECR_ACCOUNT_ID,
                                awsDeploymentTargetAccountId: QA_ACCOUNT_ID,
                                awsDeploymentRoleName: QA_DEPLOYMENT_ROLE
                }
            }
            post {
                success {
                    script {
                        if (isGithubQaComment) {
                            githubPostPrComment(repo: GITHUB_REPOSITORY, message: 'Build deployed to QA Lambda environment')
                        }
                    }
                }
            }
        }

        stage('Deploy Container to QA Fargate') {
            when {
                expression {
                    (env.BRANCH_NAME == 'master' || (isGithubQaComment && extractedRuntimeEnv == 'ecs')) && !params.RUN_TESTS
                }
            }
            steps {
                script {
                    qa_task_details = fargateDeploy environment: 'qa',
                                                    awsRegions: AWS_REGIONS,
                                                    gitCommit: env.GIT_COMMIT,
                                                    serviceName: GITHUB_REPOSITORY,
                                                    ecrRegistryAccountId: ECR_ACCOUNT_ID,
                                                    deployType: 'CREATE_TASK_DEFINITION'
                }
            }
            post {
                success {
                    script {
                        if (isGithubQaComment) {
                            githubPostPrComment(repo: GITHUB_REPOSITORY, message: 'Build deployed to QA environment, task revision number: ' + qa_task_details.revision)
                        }
                    }
                }
            }
        }
        stage('Run Tests from PR Comment') {
            when {
                expression { isGithubQaDeployAndRun }
            }
            environment {
                TAGS = "${extractedTags}"
                FRAMEWORK_ENV = "qa"
                RUNTIME_ENV = "${extractedRuntimeEnv}"
                CONFIG_ENV = "${extractedConfigEnv}"
                FRAMEWORK_RUNNER_TAG = "${env.GIT_COMMIT}"
                ADDITIONAL_ENV_VARS = "${extractedAdditionalEnvVars}"
            }
            steps {
                script {
                    def raw = env.ADDITIONAL_ENV_VARS
                    def parsed = (raw && raw != 'null' && raw.trim()) ? readJSON(text: raw) : []
                    def envMap = [:]
                    parsed.each { if (it instanceof Map) { envMap.putAll(it) } }

                    playwrightTests(
                      tags: env.TAGS,
                      configEnvironment: env.CONFIG_ENV,
                      frameworkRunnerTag: env.FRAMEWORK_RUNNER_TAG,
                      frameworkEnvironment: env.FRAMEWORK_ENV,
                      runtimeEnvironment: env.RUNTIME_ENV,
                      branchName: env.BRANCH_NAME,
                      githubPRRepo: GITHUB_REPOSITORY,
                      envVars: envMap,
                    )
                }
            }
            post {
                success {
                    githubPostCommitStatus(
                        repo: GITHUB_REPOSITORY,
                        state: 'success',
                        description: "PR tests passed (${extractedTags})",
                        context: 'jenkins/e2e-tests'
                    )
                }
                failure {
                    githubPostCommitStatus(
                        repo: GITHUB_REPOSITORY,
                        state: 'failure',
                        description: "PR tests failed (${extractedTags})",
                        context: 'jenkins/e2e-tests'
                    )
                }
            }
        }
        stage('Deploy to prod') {
            when {
                expression {
                    env.BRANCH_NAME == 'master' && !params.RUN_TESTS
                }
            }
            stages {
                // Promote the rebuilt base images as the stable baseline for
                // subsequent builds. Runs after the QA deploys (which gate it)
                // and before the prod deploys, so a prod deploy failure cannot
                // leave latest pointing at stale bases while master expects
                // the new ones. Skipped when this commit didn't change base
                // inputs (baseTag stays 'latest').
                stage('Promote base images to latest') {
                    when {
                        expression { baseTag == env.GIT_COMMIT }
                    }
                    steps {
                        retagEcrImage awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            imageName: 'lambda-test-runner-base',
                            imageTag: env.GIT_COMMIT,
                            newTag: 'latest'
                        retagEcrImage awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            imageName: 'playwright-tests-base',
                            imageTag: env.GIT_COMMIT,
                            newTag: 'latest'
                    }
                }
                stage('Deploy Lambda and ECS to prod') {
                    failFast true
                    parallel {
                        stage('Deploy container to prod Lambda') {
                            steps {
                                script {
                                    lambdaDeploy environment: 'prod',
                                                awsRegions: AWS_REGIONS,
                                                imageTag: env.GIT_COMMIT,
                                                imageName: "lambda-playwright-test-runner",
                                                functionName: "prod-playwright-tests",
                                                ecrRegistryAccountId: ECR_ACCOUNT_ID,
                                                awsDeploymentTargetAccountId: PROD_ACCOUNT_ID,
                                                awsDeploymentRoleName: PROD_DEPLOYMENT_ROLE
                                }
                            }
                        }
                        stage('Deploy Container to prod Fargate') {
                            steps {
                                script {
                                    fargateDeploy environment: 'prod',
                                                  awsRegions: AWS_REGIONS,
                                                  gitCommit: env.GIT_COMMIT,
                                                  serviceName: GITHUB_REPOSITORY,
                                                  ecrRegistryAccountId: ECR_ACCOUNT_ID,
                                                  deployType: 'CREATE_TASK_DEFINITION'
                                }
                                datadogSoftwareCatalogPublish()
                            }
                        }
                    }
                }
                stage('Deploy framework-runner to prod') {
                    steps {
                        retagEcrImage awsRegions: AWS_REGIONS,
                            ecrAccountId: ECR_ACCOUNT_ID,
                            imageName: 'playwright-framework-runner',
                            imageTag: env.GIT_COMMIT,
                            newTag: 'latest'
                    }
                }
            }
        }
        stage('Run Tests Only') {
            when {
                expression { params.RUN_TESTS }
            }
            environment {
                TAGS = "${params.TAGS}"
                FRAMEWORK_ENV = "${params.FRAMEWORK_ENV}"
                RUNTIME_ENV = "${params.RUNTIME_ENV}"
                CONFIG_ENV = "${params.CONFIG_ENV}"
                ADDITIONAL_ENV_VARS = "${params.ADDITIONAL_ENV_VARS}"
            }
            steps {
                script {
                    def raw = env.ADDITIONAL_ENV_VARS
                    def parsed = (raw && raw != 'null' && raw.trim()) ? readJSON(text: raw) : []
                    def envMap = [:]
                    parsed.each { if (it instanceof Map) { envMap.putAll(it) } }

                    playwrightTests(
                      tags: env.TAGS,
                      configEnvironment: env.CONFIG_ENV,
                      frameworkEnvironment: env.FRAMEWORK_ENV,
                      runtimeEnvironment: env.RUNTIME_ENV,
                      frameworkRunnerTag: params.FRAMEWORK_RUNNER_TAG,
                      envVars: envMap,
                    )
                }
            }
        }
    }
    post {
        failure {
            script {
                slackSend (
                    color: "danger",
                    channel: "#playwright-test-e2e-results",
                    message: """${env.JOB_NAME} - #${env.BUILD_NUMBER} Pipeline Failed! (<${env.BUILD_URL}|Open>)"""
                )
            }
        }
        fixed {
            script {
                slackSend (
                    color: "good",
                    channel: "#playwright-test-e2e-results",
                    message: """${env.JOB_NAME} - #${env.BUILD_NUMBER} Pipeline Fixed! (<${env.BUILD_URL}|Open>)"""
                )
            }
        }
    }
}
