def suites = ['threadx', 'smp']
def presets = ['Debug32', 'Release32', 'MinSizeRel32','Debug', 'Release', 'MinSizeRel']
def simulatorImageLatest = 'git.minres.com/here/here-vp:latest'
def threadxTestSimulator = '/usr/local/bin/riscv-vp'

def runOneRegression(String suite, String preset, String simulatorPath) {
  def suiteDir = "test/${suite}"
  def buildDir = "../../build/${preset}/test/${suite}"

  stage("${suite}-${preset}") {
    dir(suiteDir) {
      sh """
        set -eu
        test -n "${simulatorPath}"
        test -x "${simulatorPath}"
        cmake --fresh --preset "${preset}" \\
          -DTHREADX_TEST_SIMULATOR="${simulatorPath}"
        cmake --build --preset "${preset}" --parallel "\$(nproc)"
        ctest \\
          --test-dir "${buildDir}" \\
          --output-on-failure \\
          --output-junit "${buildDir}/ctest-results.xml" \\
          --parallel "4"
      """
    }
  }
}

def resolveImageCommit(String image) {

  withCredentials([usernamePassword(
    credentialsId: 'gitea-jenkins',
    usernameVariable: 'REGISTRY_USER',
    passwordVariable: 'REGISTRY_PASS'
  )]) {
    return sh(
      script: """
        set -eu
        echo "\$REGISTRY_PASS" | docker login "git.minres.com" -u "\$REGISTRY_USER" --password-stdin >/dev/null
        docker pull "${image}" >/dev/null
        docker image inspect --format='{{ index .Config.Labels "git-commit" }}' "${image}"
      """,
      returnStdout: true
    ).trim()
  }
}

def runRegressionLane(String image, String simulatorPath, boolean allowFailure, List suiteNames, List presetNames) {
  docker.image(image).inside {
    sh '''
      set -eu
      cmake --version
      ctest --version
    '''

    for (String suite : suiteNames) {
      def suiteName = suite
      def parallelTasks = [:]

      for (String preset : presetNames) {
        def presetName = preset
        def taskName = "${suiteName}-${presetName}"
        parallelTasks[taskName] = {
          if (allowFailure) {
            catchError(buildResult: 'UNSTABLE', stageResult: 'UNSTABLE') {
              runOneRegression(suiteName, presetName, simulatorPath)
            }
          } else {
            runOneRegression(suiteName, presetName, simulatorPath)
          }
        }
      }

      stage("${suiteName} Suite") {
        parallel parallelTasks
      }
    }
  }
}

properties([
  parameters([
    string(
      name: 'SIMULATOR_IMAGE_PINNED',
      defaultValue: 'git.minres.com/here/here-vp:4fcc192',
      description: 'Version-pinned Docker image used for the blocking regression lane'
    )
  ])
])

def canaryShouldRun = true

node {
  timestamps {
    try {
      stage('Checkout') {
        checkout scm
          sh '''
            git submodule sync --recursive
            git submodule update --init --recursive
          '''
      }

      stage('Check Canary Divergence') {
        def pinnedCommit = resolveImageCommit(params.SIMULATOR_IMAGE_PINNED)
        def latestCommit = resolveImageCommit(simulatorImageLatest)

        if (!pinnedCommit) {
          error "Missing git-commit label on ${params.SIMULATOR_IMAGE_PINNED}"
        }
        if (!latestCommit) {
          error "Missing git-commit label on ${simulatorImageLatest}"
        }

        canaryShouldRun = (pinnedCommit != latestCommit)

        if (canaryShouldRun) {
          echo "Canary enabled: ${simulatorImageLatest} (${latestCommit}) diverges from ${params.SIMULATOR_IMAGE_PINNED} (${pinnedCommit})"
        } else {
          echo "Canary skipped: ${simulatorImageLatest} and ${params.SIMULATOR_IMAGE_PINNED} both use git-commit ${pinnedCommit}"
        }
      }

      stage('Stable Regression') {
        runRegressionLane(params.SIMULATOR_IMAGE_PINNED, threadxTestSimulator, false, suites, presets)
      }

      stage('Simulator Canary') {
        if (canaryShouldRun) {
          runRegressionLane(simulatorImageLatest, threadxTestSimulator, true, suites, presets)
        } else {
          echo 'Skipping canary lane because latest and pinned images are identical.'
        }
      }
    } catch (err) {
      currentBuild.result = 'FAILURE'
      throw err
    } finally {
      junit allowEmptyResults: true, testResults: 'build/*/test/*/ctest-results.xml'
      archiveArtifacts artifacts: 'build/*/test/*/Testing/**', allowEmptyArchive: true

      if (currentBuild.currentResult == 'SUCCESS') {
        rocketSend ":thumbsup: ThreadX regression run passed, results at ${env.RUN_DISPLAY_URL} "
      } else if (currentBuild.currentResult == 'UNSTABLE') {
        rocketSend ":warning: ThreadX canary regression is unstable on ${simulatorImageLatest}, please check ${env.RUN_DISPLAY_URL} "
      } else if (currentBuild.currentResult == 'FAILURE') {
        archiveArtifacts artifacts: 'failed_seeds_*.txt', followSymlinks: false, onlyIfSuccessful: false
        rocketSend ":thumbsdown: ThreadX regression failed, please check ${env.RUN_DISPLAY_URL} "
        emailext recipientProviders: [culprits(), requestor()],
          subject: "ThreadX Pipeline Failed: ${currentBuild.fullDisplayName}",
          body: """
          <p>Build Status: ${currentBuild.currentResult}</p>
          <p> Check logs at <a href='${env.BUILD_URL}console'> Build Console Logs </a> or at <a href='${env.RUN_DISPLAY_URL}'> Overview </a></p>
          """
      }
    }
  }
}
