pipeline {
    agent any

    options {
        ansiColor('xterm')
        timestamps()
        buildDiscarder(logRotator(numToKeepStr: '15'))
        disableConcurrentBuilds()
        skipDefaultCheckout(true)
    }

    parameters {
        string(name: 'GIT_URL', defaultValue: 'file:///repos/AS400API.git', description: 'Git repository URL to clone')
        string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Branch or ref to build')
        // string(name: 'AGENT_LABEL', defaultValue: '', description: 'Optional Jenkins agent label with Docker CLI access (leave blank for default node)')
    }

    environment {
        DOTNET_CLI_TELEMETRY_OPTOUT = '1'
        DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1'
        DOTNET_NOLOGO = '1'
        BUILD_CONFIGURATION = 'Release'
        TEST_RESULTS_DIR = 'TestResults'
        COVERAGE_DIR = 'TestResults/Coverage'
        COVERAGE_FILE = 'TestResults/Coverage/coverage.opencover.xml'
        COVERAGE_HTML_DIR = 'TestResults/Coverage/html'

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

        PUBLISH_DIR = 'publish/Release'
        SONAR_PROJECT_KEY = 'AS400API'
        SONAR_PROJECT_NAME = 'AS400API'
    }

    stages {
        stage('Checkout') {
            steps {
                // Fetch the requested branch from the configured Git URL.
                script {
                    if (!params.GIT_URL?.trim()) {
                        error 'GIT_URL parameter must not be empty.'
                    }
                    String branchSpec = params.GIT_BRANCH?.trim() ?: 'main'
                    if (!branchSpec.startsWith('*/')) {
                        branchSpec = "*/${branchSpec}"
                    }
                    checkout([
                        $class                           : 'GitSCM',
                        branches                         : [[name: branchSpec]],
                        doGenerateSubmoduleConfigurations: false,
                        extensions                       : [[$class: 'CloneOption', depth: 0, noTags: false, shallow: false]],
                        userRemoteConfigs                : [[url: params.GIT_URL.trim()]]
                    ])
                }
            }
        }

        stage('.NET SDK Info') {
            steps {
                // Sanity check the SDK and install required global tools (Java for Sonar + dotnet-sonarscanner).
                sh '''#!/bin/bash -e
                apt-get update
                apt-get install -y --no-install-recommends 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 "Installing fallback ICU development package..."
                        apt-get install -y --no-install-recommends libicu-dev
                    fi
                fi
                rm -rf /var/lib/apt/lists/*

                # 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"
                

                echo '### dotnet --info'
                dotnet --info

                echo '### Install/Update dotnet tooling'
                dotnet tool install --global dotnet-sonarscanner || dotnet tool update --global dotnet-sonarscanner
                dotnet tool install --global dotnet-reportgenerator-globaltool || dotnet tool update --global dotnet-reportgenerator-globaltool
                '''
                script {
                    if (!env.PATH.contains('/root/.dotnet/tools')) {
                        env.PATH = "${env.PATH}:/root/.dotnet/tools"
                    }
                }
            }
        }

        stage('Restore') {
            steps {
                // Restore solution dependencies.
                sh '''#!/bin/bash -e
                dotnet restore AS400API.sln
                '''
            }
        }

        stage('Build') {
            steps {
                // Build the solution in Release mode without failing on warnings.
                sh '''#!/bin/bash -e
                dotnet build AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-restore
                '''
            }
        }

        stage('Test & Coverage') {
            steps {
                // Execute tests once for fast feedback and capture TRX + OpenCover reports.
                sh '''#!/bin/bash -e
                rm -rf "${TEST_RESULTS_DIR}"
                mkdir -p "${COVERAGE_DIR}"
                mkdir -p "${COVERAGE_HTML_DIR}"
                dotnet test AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-build \
                    /p:CollectCoverage=true \
                    /p:CoverletOutputFormat=opencover \
                    /p:CoverletOutput="${COVERAGE_DIR}/" \
                    --results-directory "${TEST_RESULTS_DIR}" \
                    --logger "trx;LogFileName=test-results.trx" \
                    --logger "junit;LogFileName=test-results.xml"

                dotnet tool run reportgenerator \
                    -reports:"${COVERAGE_FILE}" \
                    -targetdir:"${COVERAGE_HTML_DIR}" \
                    -reporttypes:"HtmlInline_AzurePipelines;Cobertura"
                '''
                junit testResults: "${TEST_RESULTS_DIR}/**/*.xml", allowEmptyResults: false
                publishHTML(target: [
                    allowMissing           : false,
                    keepAll                : true,
                    reportDir              : "${COVERAGE_HTML_DIR}",
                    reportFiles            : 'index.html',
                    reportName             : 'Code Coverage'
                ])
            }
        }

        stage('SonarQube: Begin') {
            steps {
                // Kick off Sonar analysis with project metadata and coverage location.
                script {
                    env.SONAR_PROJECT_VERSION = env.BUILD_NUMBER ?: '0.0.0'
                }
                withSonarQubeEnv('sonarqube') {
                    sh '''#!/bin/bash -e
                    dotnet sonarscanner begin \
                        /k:"${SONAR_PROJECT_KEY}" \
                        /n:"${SONAR_PROJECT_NAME}" \
                        /v:"${SONAR_PROJECT_VERSION}" \
                        /d:sonar.host.url="${SONAR_HOST_URL}" \
                        /d:sonar.login="${SONAR_AUTH_TOKEN}" \
                        /d:sonar.cs.opencover.reportsPaths="${COVERAGE_FILE}"
                    '''
                }
            }
        }

        stage('Rebuild for Sonar') {
            steps {
                // Run a rebuild under the Sonar scanner context so analysis picks up compilation data.
                sh '''#!/bin/bash -e
                dotnet build AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-restore --no-incremental
                '''
            }
        }

        stage('SonarQube: End') {
            steps {
                // Close the Sonar analysis and push data to the server.
                withSonarQubeEnv('sonarqube') {
                    sh '''#!/bin/bash -e
                    dotnet sonarscanner end /d:sonar.login="${SONAR_AUTH_TOKEN}"
                    '''
                }
            }
        }

        stage('Quality Gate') {
            steps {
                // Wait for the Quality Gate result and fail fast on non-green outcomes.
                timeout(time: 15, unit: 'MINUTES') {
                    script {
                        def qualityGate = waitForQualityGate()
                        if (qualityGate.status != 'OK') {
                            error "SonarQube Quality Gate failed: ${qualityGate.status}"
                        }
                    }
                }
            }
        }

        stage('Archive') {
            steps {
                // Publish Release-ready binaries and test artifacts for downstream consumption.
                sh '''#!/bin/bash -e
                rm -rf "${PUBLISH_DIR}"
                dotnet publish AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-restore --output "${PUBLISH_DIR}"
                '''
                archiveArtifacts artifacts: "${PUBLISH_DIR}/**", fingerprint: true
                archiveArtifacts artifacts: "${TEST_RESULTS_DIR}/**/*.trx", allowEmptyArchive: true
                archiveArtifacts artifacts: "${COVERAGE_FILE}", allowEmptyArchive: true
                archiveArtifacts artifacts: "${COVERAGE_HTML_DIR}/**", allowEmptyArchive: true
            }
        }
    }

    post {
        always {
            script {
                try {
                    deleteDir()
                } catch (org.jenkinsci.plugins.workflow.steps.MissingContextVariableException ignore) {
                    echo 'Workspace cleanup skipped because no workspace was provisioned.'
                }
            }
        }
    }
}
