pipeline {
  agent any

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

  environment {
    // ถ้าใช้ local bare repo
    GIT_URL = 'file:///repos/AS400API.git'

    // Path ติดตั้ง dotnet ชั่วคราวใน pipeline
    DOTNET_ROOT = "${WORKSPACE}/.dotnet"
    PATH = "${DOTNET_ROOT}:${PATH}"

    // Dependency-Check cache
    DC_DATA = "${JENKINS_HOME}/.dc-cache"

    // SonarQube
    SONARQUBE_INSTANCE = 'SonarQube'
    SONAR_PROJECT_KEY  = 'AS400API'
  }

  stages {


    

    stage('Checkout') {
      steps {
        checkout([$class: 'GitSCM',
          branches: [[name: '*/main']],
          userRemoteConfigs: [[url: "${GIT_URL}"]]
        ])
      }
    }

    


    stage('Install prerequisites') {
      steps {
        sh '''
          set -euo pipefail
          if command -v apt-get >/dev/null 2>&1; then
            export DEBIAN_FRONTEND=noninteractive
            apt-get update
            apt-get install -y --no-install-recommends \
              ca-certificates curl unzip jq git openjdk-21-jre-headless

            # --- Install ICU runtime (Debian uses versioned package names) ---
            if ! ldconfig -p | grep -qi libicu; then
              PKG="$(apt-cache search '^libicu[0-9]+$' | awk '{print $1}' | head -n1 || true)"
              if [ -n "$PKG" ]; then
                echo "Installing ICU package: ${PKG}"
                apt-get install -y --no-install-recommends "${PKG}"
              else
                echo "Falling back to libicu-dev..."
                apt-get install -y --no-install-recommends libicu-dev
              fi
            fi
          fi

          # Install .NET SDK locally for the build user
          mkdir -p "${WORKSPACE}/.dotnet"
          curl -fsSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
          bash dotnet-install.sh --channel 9.0 --install-dir "${WORKSPACE}/.dotnet"
          bash dotnet-install.sh --channel 8.0 --install-dir "${WORKSPACE}/.dotnet" 

          export PATH="${WORKSPACE}/.dotnet:${PATH}"
          dotnet --info
        '''
      }
    }

    stage('SCA (NuGet + OWASP)') {
      steps {
        sh '''
          set -euo pipefail
          echo "=== NuGet vulnerability audit ==="
          dotnet restore
          dotnet list package --vulnerable || true

          echo "=== OWASP Dependency-Check ==="
          rm -rf depcheck dependency-check
          mkdir -p depcheck

          API="https://api.github.com/repos/jeremylong/DependencyCheck/releases/latest"
          echo "Resolving latest Dependency-Check..."
          ASSET_URL=$(curl -fsSL "$API" | jq -r '.assets[]?.browser_download_url | select(test("release\\\\.zip$"))' | head -n1)
          echo "Downloading: $ASSET_URL"
          curl -fL --retry 3 --retry-all-errors -o depcheck.zip "$ASSET_URL"
          unzip -oq depcheck.zip -d dependency-check

          DC_BIN="dependency-check/dependency-check/bin/dependency-check.sh"
          bash "$DC_BIN" --data "${DC_DATA}" --updateonly || true
          bash "$DC_BIN" -f HTML -f XML \
            --project "AS400API" \
            --scan . \
            --out depcheck \
            --data "${DC_DATA}" \
            --noupdate || true \
            --disableAssembly   
        '''
      }
      post {
        always {
          archiveArtifacts artifacts: 'depcheck/**', allowEmptyArchive: true
          script {
            try {
              publishHTML(target: [
                reportName: 'OWASP Dependency-Check',
                reportDir: 'depcheck',
                reportFiles: 'dependency-check-report.html',
                keepAll: true,
                alwaysLinkToLastBuild: true,
                allowMissing: true
              ])
            } catch (Throwable e) {
              echo "Skipping HTML report publish: ${e.getClass().getSimpleName()}"
            }
          }
        }
      }
    }

    stage('SAST with SonarQube') {
      steps {
        withSonarQubeEnv('SonarQube') {
          sh '''
            set -euo pipefail
            echo "=== SAST with SonarQube ==="

            # Ensure scanner is available and PATH includes global tools
            dotnet tool update --global dotnet-sonarscanner
            export PATH="$PATH:/root/.dotnet/tools"

            # Optional: show Java & reachability
            java -version || true
            # ไม่ต้องใช้ curl เพราะ API นี้ติดเรื่องสิทธิ์
            # curl -sf "$SONAR_HOST_URL/api/system/health" || { echo "Cannot reach SonarQube at $SONAR_HOST_URL"; exit 1; }

            # BEGIN (use injected URL/token; add verbose for diagnostics)
            dotnet sonarscanner begin \
              /k:AS400API \
              /d:sonar.verbose=true \
              /d:sonar.exclusions=**/bin/**,**/obj/** \
              /d:sonar.test.exclusions=**/*.Tests/** \
              /d:sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml

            # BUILD & TEST (produce OpenCover report)
            dotnet restore
            dotnet build -c Release
            dotnet test AS400API.Tests/AS400API.Tests.csproj -c Release \
              /p:CollectCoverage=true \
              /p:CoverletOutput=./TestResults/coverage/ \
              /p:CoverletOutputFormat=opencover

            # END (no login flag; uses env from withSonarQubeEnv)
            # dotnet sonarscanner end /d:sonar.verbose=true
            dotnet sonarscanner end
          '''
        }

        // Optionally wait for the Quality Gate (only if 'end' succeeds)
        // waitForQualityGate abortPipeline: true
      }
    }
    stage('Test + Coverage') {
      steps {
        sh '''
          set -euo pipefail
          dotnet build -c Debug
          dotnet test \
            --logger "junit;LogFileName=test-results.xml" \
            --results-directory "TestResults" \
            /p:CollectCoverage=true \
            /p:CoverletOutput=coverage/ \
            /p:CoverletOutputFormat=cobertura

          mkdir -p coverage-report
          COB=$(find . -type f -name "coverage.cobertura.xml" | head -n1 || true)
          if [ -n "$COB" ]; then
            cp "$COB" coverage-report/Cobertura.xml
          fi
        '''
      }
      post {
        always {
          junit allowEmptyResults: false, testResults: '**/TestResults/**/*.xml'
          archiveArtifacts artifacts: 'coverage-report/**', allowEmptyArchive: true
        }
      }
    }

    stage('Build') {
      steps {
        sh '''
          set -euo pipefail
          # build only the app project
          dotnet restore AS400API.csproj
          dotnet build AS400API.csproj -c Release -warnaserror:false -p:TreatWarningsAsErrors=false

          # publish the app project (not the solution)
          dotnet publish AS400API.csproj -c Release -o out --no-build
        '''
      }
      post {
        success {
          archiveArtifacts artifacts: 'out/**', allowEmptyArchive: false
        }
      }
    }

    stage('Quality Gate') {
      steps {
        timeout(time: 30, unit: 'MINUTES') {
          waitForQualityGate abortPipeline: true
        }
      }
    }
  } // end stages

  post {
    always {
      echo "Pipeline finished (status: ${currentBuild.currentResult})"
    }
  }
} // end pipeline