def baseBuildImage = "icr.io/streamsets_private/watsonx-di-sdk"
def buildImageTag = "latest"
def buildImage = "${baseBuildImage}:${buildImageTag}"
def failedStages = []
def integrationTestSummary = [:]
def integrationOnPremTestSummary = [:]
def unitTestResults_311 = ''
def unitTestResults_312 = ''
def unitTestSummary = [:]
def docsTestSummary = [:]
def docsCheckSummary = [:]
def toolDiscoveryToolsTestSummary = [:]
def toolDiscoveryResourcesTestSummary = [:]
def unstableStages = []
def testStagesRun = [:]
def testStagesStatus = [:]

pipeline {
    parameters {
        booleanParam(
            name: 'RUN_TOOL_DISCOVERY_TOOLS_TESTS',
            defaultValue: false,
            description: 'Run Tool Discovery Tests for MCP Tools'
        )
        booleanParam(
            name: 'RUN_TOOL_DISCOVERY_RESOURCES_TESTS',
            defaultValue: false,
            description: 'Run Tool Discovery Tests for MCP Resources'
        )
        booleanParam(
            name: 'RUN_INTEGRATION_TESTS',
            defaultValue: false,
            description: 'Run Integration Tests for MCP'
        )
    }
    options {
        timestamps ()
        ansiColor('xterm')
    }
    agent {
        label 'podman-4g-4c'
    }
    stages {
        stage('Lint & Format') {
            steps {
                catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                    withEnv(["HOME=${env.WORKSPACE}"]) {
                        script {
                            sh 'uv run poe lint-evergreen && uv run poe format-evergreen'
                        }
                    }
                }
            }
            post {
                unstable {
                    script {
                        unstableStages.add('Lint & Format')
                    }
                }
            }
        }
        stage('Unit Tests') {
            environment {
                UV_LINK_MODE = 'copy'
            }
            steps {
                catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                    withEnv(["HOME=${env.WORKSPACE}"]) {
                        script {
                            def pythonVersions = ['3.11', '3.12']
                            pythonVersions.each { pyVersion ->
                                def pyVersionShort = pyVersion.replace('.', '')
                                sh """
                                    uv run poe unit${pyVersionShort}
                                """
                            }
                        }
                    }
                }
            }
            post {
                always {
                    script {
                        testStagesRun['Unit Tests'] = true
                        unitTestResults_311 = junit testResults: 'results_unit_py311.xml'
                        unitTestResults_312 = junit testResults: 'results_unit_py312.xml'
                    }
                }
                success {
                    script {
                        testStagesStatus['Unit Tests'] = 'PASSED'
                    }
                    recordCoverage(
                        tools: [[parser: 'COBERTURA', pattern: '**/coverage_unit_py311.xml']],
                        id: 'Py311_Unit_Tests',
                        name: 'Unit Tests (Python 3.11)'
                    )
                    recordCoverage(
                        tools: [[parser: 'COBERTURA', pattern: '**/coverage_unit_py312.xml']],
                        id: 'Py312_Unit_Tests',
                        name: 'Unit Tests (Python 3.12)'
                    )
                }
                failure {
                    script {
                        testStagesStatus['Unit Tests'] = 'FAILED'
                        boolean unitTestsFailed = false
                        // This logic is for Slack notifications
                        if (unitTestResults_311.failCount) {
                            unitTestsFailed = true
                            unitTestSummary.put('Python 3.11', ['total': unitTestResults_311.totalCount,
                                                               'passing': unitTestResults_311.passCount,
                                                               'failing': unitTestResults_311.failCount,
                                                               'skipped': unitTestResults_311.skipCount])
                        }
                        if (unitTestResults_312.failCount) {
                            unitTestsFailed = true
                            unitTestSummary.put('Python 3.12', ['total': unitTestResults_312.totalCount,
                                                                'passing': unitTestResults_312.passCount,
                                                                'failing': unitTestResults_312.failCount,
                                                                'skipped': unitTestResults_312.skipCount])
                        }
                        if (unitTestsFailed) {
                            failedStages.add('Unit Tests')
                        }

                    }
                }
            }
        }
        stage('Tool Discovery Tests - Tools') {
            when {
                anyOf {
                    expression { params.RUN_TOOL_DISCOVERY_TOOLS_TESTS == true }
                    expression { hasChangesIn("src/di_mcp/tools/**") }
                    expression { hasChangesIn("tests/tool_discovery/tools/**") }
                }
                beforeAgent true
            }
            steps {
                catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                    script {
                        def envVars = [
                            "HOME=${env.WORKSPACE}",
                        ]
                        def ibm_cloud_api_key_cred_id

                        if (params.environment == "preprod") {
                            envVars.add("API_BASE_URL=https://api.dai.test.cloud.ibm.com")
                            envVars.add("IBM_CLOUD_URL=https://cloud.ibm.com")
                            ibm_cloud_api_key_cred_id = "sx-automation-sdi-dai-test-paid-account-api-key"
                        } else {
                            envVars.add("API_BASE_URL=https://api.dai.dev.cloud.ibm.com")
                            envVars.add("IBM_CLOUD_URL=https://test.cloud.ibm.com")
                            ibm_cloud_api_key_cred_id = "sx-automation-sdi-dai-dev-paid-account-api-key"
                        }

                        withEnv(envVars) {
                            withCredentials([
                                string(credentialsId: ibm_cloud_api_key_cred_id, variable: 'IBM_CLOUD_API_KEY')
                            ]) {
                                sh 'uv run poe test-tool-discovery-tools'
                            }
                        }
                    }
                }
            }
            post {
                always {
                    script {
                        testStagesRun['Tool Discovery Tests - Tools'] = true
                        def toolDiscoveryToolsTestResults = junit testResults: 'results_tool_discovery_tools.xml', allowEmptyResults: true
                        if (toolDiscoveryToolsTestResults) {
                            toolDiscoveryToolsTestSummary = [
                                'total': toolDiscoveryToolsTestResults.totalCount,
                                'passing': toolDiscoveryToolsTestResults.passCount,
                                'failing': toolDiscoveryToolsTestResults.failCount,
                                'skipped': toolDiscoveryToolsTestResults.skipCount
                            ]
                        }
                    }
                }
                success {
                    script {
                        testStagesStatus['Tool Discovery Tests - Tools'] = 'PASSED'
                    }
                }
                failure {
                    script {
                        testStagesStatus['Tool Discovery Tests - Tools'] = 'FAILED'
                        if (toolDiscoveryToolsTestSummary.failing > 0) {
                            failedStages.add('Tool Discovery Tests - Tools')
                        }
                    }
                }
            }
        }
        stage('Tool Discovery Tests - Resources') {
            when {
                anyOf {
                    expression { params.RUN_TOOL_DISCOVERY_RESOURCES_TESTS == true }
                    expression { hasChangesIn("src/di_mcp/resources/**") }
                    expression { hasChangesIn("tests/tool_discovery/resources/**") }
                }
                beforeAgent true
            }
            steps {
                catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                    script {
                        def envVars = [
                            "HOME=${env.WORKSPACE}",
                        ]
                        def ibm_cloud_api_key_cred_id

                        if (params.environment == "preprod") {
                            envVars.add("API_BASE_URL=https://api.dai.test.cloud.ibm.com")
                            envVars.add("IBM_CLOUD_URL=https://cloud.ibm.com")
                            ibm_cloud_api_key_cred_id = "sx-automation-sdi-dai-test-paid-account-api-key"
                        } else {
                            envVars.add("API_BASE_URL=https://api.dai.dev.cloud.ibm.com")
                            envVars.add("IBM_CLOUD_URL=https://test.cloud.ibm.com")
                            ibm_cloud_api_key_cred_id = "sx-automation-sdi-dai-dev-paid-account-api-key"
                        }

                        withEnv(envVars) {
                            withCredentials([
                                string(credentialsId: ibm_cloud_api_key_cred_id, variable: 'IBM_CLOUD_API_KEY')
                            ]) {
                                sh 'uv run poe test-tool-discovery-resources'
                            }
                        }
                    }
                }
            }
            post {
                always {
                    script {
                        testStagesRun['Tool Discovery Tests - Resources'] = true
                        def toolDiscoveryResourcesTestResults = junit testResults: 'results_tool_discovery_resources.xml', allowEmptyResults: true
                        if (toolDiscoveryResourcesTestResults) {
                            toolDiscoveryResourcesTestSummary = [
                                'total': toolDiscoveryResourcesTestResults.totalCount,
                                'passing': toolDiscoveryResourcesTestResults.passCount,
                                'failing': toolDiscoveryResourcesTestResults.failCount,
                                'skipped': toolDiscoveryResourcesTestResults.skipCount
                            ]
                        }
                    }
                }
                success {
                    script {
                        testStagesStatus['Tool Discovery Tests - Resources'] = 'PASSED'
                    }
                }
                failure {
                    script {
                        testStagesStatus['Tool Discovery Tests - Resources'] = 'FAILED'
                        if (toolDiscoveryResourcesTestSummary.failing > 0) {
                            failedStages.add('Tool Discovery Tests - Resources')
                        }
                    }
                }
            }
        }
        stage('Integration Tests - Claude') {
            when {
                anyOf {
                    expression { params.RUN_INTEGRATION_TESTS == true }
                }
                beforeAgent true
            }
            steps {
                catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
                    script {
                        def envVars = [
                            "HOME=${env.WORKSPACE}",
                        ]
                        def ibm_cloud_api_key_cred_id

                        if (params.environment == "preprod") {
                            envVars.add("API_BASE_URL=https://api.dai.test.cloud.ibm.com")
                            envVars.add("IBM_CLOUD_URL=https://cloud.ibm.com")
                            ibm_cloud_api_key_cred_id = "sx-automation-sdi-dai-test-paid-account-api-key"
                        } else {
                            envVars.add("API_BASE_URL=https://api.dai.dev.cloud.ibm.com")
                            envVars.add("IBM_CLOUD_URL=https://test.cloud.ibm.com")
                            ibm_cloud_api_key_cred_id = "sx-automation-sdi-dai-dev-paid-account-api-key"
                        }

                        withEnv(envVars) {
                            withCredentials([
                                string(credentialsId: ibm_cloud_api_key_cred_id, variable: 'IBM_CLOUD_API_KEY'),
                                usernamePassword(
                                    credentialsId: "watsonx-bedrock-streamsets-eng-team-ee-jenkins-di-mcp",
                                    usernameVariable: 'AWS_ACCESS_KEY_ID',
                                    passwordVariable: 'AWS_SECRET_ACCESS_KEY'
                                )
                            ]) {
                                sh 'uv run --group integration-remote poe integration-claude'
                            }
                        }
                    }
                }
            }
            post {
                always {
                    script {
                        testStagesRun['Integration Tests - Claude'] = true
                        def integrationOnClaudeTestResults = junit testResults: 'results_integration_claude.xml', allowEmptyResults: true
                        if (integrationOnClaudeTestResults) {
                            integrationTestSummary = [
                                'total': integrationOnClaudeTestResults.totalCount,
                                'passing': integrationOnClaudeTestResults.passCount,
                                'failing': integrationOnClaudeTestResults.failCount,
                                'skipped': integrationOnClaudeTestResults.skipCount
                            ]
                        }
                    }
                }
                success {
                    script {
                        testStagesStatus['Integration Tests - Claude'] = 'PASSED'
                    }
                }
                failure {
                    script {
                        testStagesStatus['Integration Tests - Claude'] = 'FAILED'
                        if (integrationTestSummary.failing > 0) {
                            failedStages.add('Integration Tests - Claude')
                        }
                    }
                }
            }
        }
    }
    post {
        cleanup {
            cleanWs()
        }
        always {
            script {
                // Post GitHub PR comment with test results if this is a PR build
                if (env.CHANGE_ID) {
                    postGitHubPRComment(
                        testStagesRun,
                        testStagesStatus,
                        unitTestSummary,
                        integrationTestSummary,
                        integrationOnPremTestSummary,
                        docsCheckSummary,
                        docsTestSummary,
                        toolDiscoveryToolsTestSummary,
                        toolDiscoveryResourcesTestSummary,
                        failedStages,
                        unstableStages
                    )
                }
            }
        }
    }
}

def String buildTestResultsMarkdown(stagesRun, stagesStatus, unitSummary, integrationSummary, integrationOnPremSummary, docsCheckSummary, docsTestSummary, toolDiscoveryToolsSummary, toolDiscoveryResourcesSummary, failed, unstable) {
    def markdown = "## 🧪 Test Results Summary\n\n"

    // Overall status
    def overallStatus = currentBuild.result ?: 'SUCCESS'
    def statusEmoji = overallStatus == 'SUCCESS' ? '✅' : (overallStatus == 'UNSTABLE' ? '⚠️' : '❌')
    markdown += "**Overall Status:** ${statusEmoji} ${overallStatus}\n\n"

    // Build info
    markdown += "**Build:** [#${env.BUILD_NUMBER}](${env.BUILD_URL})\n"
    markdown += "**Branch:** `${env.BRANCH_NAME}`\n\n"

    // Test stages summary table
    if (stagesRun.size() > 0) {
        markdown += "### Test Stages\n\n"
        markdown += "| Stage | Status | Details |\n"
        markdown += "|-------|--------|----------|\n"

        stagesRun.each { stageName, wasRun ->
            if (wasRun) {
                def status = stagesStatus[stageName] ?: 'UNKNOWN'
                def statusIcon = status == 'PASSED' ? '✅' : '❌'
                def detailsLink = "[View Logs](${env.BUILD_URL}console)"
                markdown += "| ${stageName} | ${statusIcon} ${status} | ${detailsLink} |\n"
            }
        }
        markdown += "\n"
    }

    // Unit Tests Details
    if (stagesRun['Unit Tests']) {
        markdown += "<details>\n"
        markdown += "<summary><b>📊 Unit Tests Details</b></summary>\n\n"

        if (stagesStatus['Unit Tests'] == 'FAILED') {
            // Show failed versions
            markdown += "#### Failed Python Versions\n\n"
            markdown += "| Python Version | Total | Passing | Failing | Skipped |\n"
            markdown += "|----------------|-------|---------|---------|----------|\n"
            unitSummary.each { version, results ->
                markdown += "| ${version} | ${results.total} | ${results.passing} | **${results.failing}** | ${results.skipped} |\n"
            }
        } else if (stagesStatus['Unit Tests'] == 'PASSED') {
            markdown += "All unit tests passed across Python 3.11 and 3.12! 🎉\n"
        }

        markdown += "\n[View Full Unit Test Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Integration Tests Details
    if (stagesRun['Integration Tests']) {
        markdown += "<details>\n"
        markdown += "<summary><b>🔗 Integration Tests Details</b></summary>\n\n"

        if (integrationSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${integrationSummary.total} |\n"
            markdown += "| Passing | ${integrationSummary.passing} |\n"

            if (integrationSummary.failing > 0) {
                markdown += "| **Failing** | **${integrationSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${integrationSummary.failing} |\n"
            }

            markdown += "| Skipped | ${integrationSummary.skipped} |\n"
        }

        markdown += "\n[View Full Integration Test Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Integration Tests On-Prem Details
    if (stagesRun['Integration Tests On-Prem']) {
        markdown += "<details>\n"
        markdown += "<summary><b>🏢 Integration Tests On-Prem Details</b></summary>\n\n"

        if (integrationOnPremSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${integrationOnPremSummary.total} |\n"
            markdown += "| Passing | ${integrationOnPremSummary.passing} |\n"

            if (integrationOnPremSummary.failing > 0) {
                markdown += "| **Failing** | **${integrationOnPremSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${integrationOnPremSummary.failing} |\n"
            }

            markdown += "| Skipped | ${integrationOnPremSummary.skipped} |\n"
        }

        markdown += "\n[View Full On-Prem Test Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Documentation Check Details
    if (stagesRun['Documentation Check']) {
        markdown += "<details>\n"
        markdown += "<summary><b>📝 Documentation Check Details</b></summary>\n\n"

        if (docsCheckSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${docsCheckSummary.total} |\n"
            markdown += "| Passing | ${docsCheckSummary.passing} |\n"

            if (docsCheckSummary.failing > 0) {
                markdown += "| **Failing** | **${docsCheckSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${docsCheckSummary.failing} |\n"
            }

            markdown += "| Skipped | ${docsCheckSummary.skipped} |\n"
        }

        markdown += "\n[View Full Documentation Check Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Documentation Tests Details
    if (stagesRun['Documentation Tests']) {
        markdown += "<details>\n"
        markdown += "<summary><b>📚 Documentation Tests Details</b></summary>\n\n"

        if (docsTestSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${docsTestSummary.total} |\n"
            markdown += "| Passing | ${docsTestSummary.passing} |\n"

            if (docsTestSummary.failing > 0) {
                markdown += "| **Failing** | **${docsTestSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${docsTestSummary.failing} |\n"
            }

            markdown += "| Skipped | ${docsTestSummary.skipped} |\n"
        }

        markdown += "\n[View Full Documentation Test Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Tool Discovery Tests - Tools Details
    if (stagesRun['Tool Discovery Tests - Tools']) {
        markdown += "<details>\n"
        markdown += "<summary><b>🔧 Tool Discovery Tests - Tools Details</b></summary>\n\n"

        if (toolDiscoveryToolsSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${toolDiscoveryToolsSummary.total} |\n"
            markdown += "| Passing | ${toolDiscoveryToolsSummary.passing} |\n"

            if (toolDiscoveryToolsSummary.failing > 0) {
                markdown += "| **Failing** | **${toolDiscoveryToolsSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${toolDiscoveryToolsSummary.failing} |\n"
            }

            markdown += "| Skipped | ${toolDiscoveryToolsSummary.skipped} |\n"
        }

        markdown += "\n[View Full Tool Discovery Tests Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Tool Discovery Tests - Resources Details
    if (stagesRun['Tool Discovery Tests - Resources']) {
        markdown += "<details>\n"
        markdown += "<summary><b>📦 Tool Discovery Tests - Resources Details</b></summary>\n\n"

        if (toolDiscoveryResourcesSummary.size() > 0) {
            markdown += "| Metric | Count |\n"
            markdown += "|--------|-------|\n"
            markdown += "| Total | ${toolDiscoveryResourcesSummary.total} |\n"
            markdown += "| Passing | ${toolDiscoveryResourcesSummary.passing} |\n"

            if (toolDiscoveryResourcesSummary.failing > 0) {
                markdown += "| **Failing** | **${toolDiscoveryResourcesSummary.failing}** |\n"
            } else {
                markdown += "| Failing | ${toolDiscoveryResourcesSummary.failing} |\n"
            }

            markdown += "| Skipped | ${toolDiscoveryResourcesSummary.skipped} |\n"
        }

        markdown += "\n[View Full Tool Discovery Tests Results](${env.BUILD_URL}testReport/)\n"
        markdown += "</details>\n\n"
    }

    // Failed stages summary
    if (failed.size() > 0) {
        markdown += "### ❌ Failed Stages\n\n"
        failed.each { stage ->
            markdown += "- **${stage}** - [View Logs](${env.BUILD_URL}console)\n"
        }
        markdown += "\n"
    }

    // Unstable stages summary
    if (unstable.size() > 0) {
        markdown += "### ⚠️ Unstable Stages\n\n"
        unstable.each { stage ->
            markdown += "- **${stage}** - [View Logs](${env.BUILD_URL}console)\n"
        }
        markdown += "\n"
    }

    markdown += "---\n"
    markdown += "*Generated by Jenkins CI*"

    return markdown
}

def void postGitHubPRComment(stagesRun, stagesStatus, unitSummary, integrationSummary, integrationOnPremSummary, docsCheckSummary, docsTestSummary, toolDiscoveryToolsSummary, toolDiscoveryResourcesSummary, failed, unstable) {
    try {
        def commentBody = buildTestResultsMarkdown(
            stagesRun,
            stagesStatus,
            unitSummary,
            integrationSummary,
            integrationOnPremSummary,
            docsCheckSummary,
            docsTestSummary,
            toolDiscoveryToolsSummary,
            toolDiscoveryResourcesSummary,
            failed,
            unstable
        )

        publishChecks(
            name: 'Test Results',
            title: 'Test Execution Summary',
            text: commentBody,
            conclusion: currentBuild.result == 'SUCCESS' ? 'SUCCESS' : (currentBuild.result == 'UNSTABLE' ? 'NEUTRAL' : 'FAILURE'),
            detailsURL: "${env.BUILD_URL}"
        )

        echo "Posted test results to GitHub PR #${env.CHANGE_ID}"
    } catch (Exception e) {
        echo "Failed to post GitHub PR comment: ${e.message}"
        echo "This is non-fatal, continuing..."
    }
}

def boolean hasChangesIn(String module) {
    // we need to compare against the HEAD of the dev branch if we are on a different branch
    // or we need to compare against the previous commit if we are on the dev branch
    // since it barely takes any time, we'll just run both comparisons and see if either is true
    def compareAgainstDevCommand = "git diff --name-only origin/main HEAD '${module}' | cat"
    def compareAgainstPreviousCommitCommand = "git diff-tree --no-commit-id --name-only -r ${env.GIT_COMMIT} -- '${module}'"

    def outputAgainstDev = sh(script: compareAgainstDevCommand, returnStdout: true).trim()
    def outputAgainstPrevious = sh(script: compareAgainstPreviousCommitCommand, returnStdout: true).trim()

    if (outputAgainstDev || outputAgainstPrevious) {
        echo "Changes detected in module: '${module}'"
        return true
    } else {
        return false
    }
}
