@Library('cicd-lib@0.23') _

import python.VirtualEnvironment
import python.VEnvManager
import pytest.TestSession
import pytest.TestGroup
import pytest.PyTestManager
import pytest.PyTestParams

def SW_NODE = 'windows-slave'
def ECAT_NODE = "ecat-test"
def ECAT_NODE_LOCK = "test_execution_lock_ecat"
def CAN_NODE = "canopen-test"
def CAN_NODE_LOCK = "test_execution_lock_can"
def SIRIUS_NODE = "RA-RD-CT-SIRIUS"

def WIN_DOCKER_IMAGE = 'ingeniacontainers.azurecr.io/win-python-builder:1.9'
def DOCKER_TMP_PATH = "C:\\Users\\ContainerAdministrator\\summit_drives_ci_configs"
def PUBLISHER_DOCKER_IMAGE = "ingeniacontainers.azurecr.io/publisher:1.8"
def LIN_DOCKER_IMAGE = 'ingeniacontainers.azurecr.io/docker-python:1.7'
def LIN_DOCKER_TMP_PATH = "/tmp/summit_drives_ci_configs"

def DEFAULT_PYTHON_VERSION = "3.12"
def BRANCH_NAME_MASTER = 'main'
def DEFAULT_LOGGING_LEVEL = 'INFO'

// Define branches to use for compatibility repository tests
def INGM_COMPATIBILITY_BRANCH = 'develop'
def INGW_COMPATIBILITY_BRANCH = 'develop'


VEnvManager venvManager = new VEnvManager(
    pipeline: this,
    default_python_version: DEFAULT_PYTHON_VERSION,
    poetry_default_install_command: 'poetry sync --all-groups --no-root'
)

PyTestManager ingmTestManager = new PyTestManager(pipeline: this, venvManager: venvManager)
PyTestManager ingwTestManager = new PyTestManager(pipeline: this, venvManager: venvManager)

TestSession TEST_SESSIONS = new TestSession(
    useCoverage: false,
    startWiresharkTimeoutS: 10.0,
    importMode: "importlib",
    enableFirmwareVersionCheck: true,
    setAttApiToken: true,
    runTestBaseCommand: 'poetry run poe tests -s -vv -o pythonpath=.',
)

TestSession INGM_TEST_SESSIONS = TEST_SESSIONS.override()
TestGroup INGM_CAN_TESTS = ingmTestManager.createGroup("CAN_TEST_SESSIONS", INGM_TEST_SESSIONS.override())
TestGroup INGM_ETH_TESTS = ingmTestManager.createGroup("ETH_TEST_SESSIONS", INGM_TEST_SESSIONS.override())
TestGroup INGM_ECAT_TESTS = ingmTestManager.createGroup("ECAT_TEST_SESSIONS", INGM_TEST_SESSIONS.override())
TestGroup INGM_SIRIUS_TESTS = ingmTestManager.createGroup("SIRIUS_TEST_SESSIONS", INGM_TEST_SESSIONS.override())

TestSession INGW_TEST_SESSIONS = TEST_SESSIONS.override()
TestGroup INGW_CAN_TESTS = ingwTestManager.createGroup("CAN_TEST_SESSIONS", INGW_TEST_SESSIONS.override())
TestGroup INGW_ECAT_TESTS = ingwTestManager.createGroup("ECAT_TEST_SESSIONS", INGW_TEST_SESSIONS.override())
TestGroup INGW_SIRIUS_TESTS = ingwTestManager.createGroup("SIRIUS_TEST_SESSIONS", INGW_TEST_SESSIONS.override())

def cleanUntrackedFiles() {
    if (isUnix()) {
        sh 'git clean -fdx'
    } else {
        bat "git clean -fdx"
    }
}

def checkoutRepository(String url, String branch, String folder, String credentialsId) {
    dir(folder) {
        checkout([
            $class: 'GitSCM',
            branches: [[name: branch]],
            doGenerateSubmoduleConfigurations: false,
            extensions: [
                [$class: 'SubmoduleOption',
                    disableSubmodules: false,
                    parentCredentials: true,
                    recursiveSubmodules: true,
                    reference: '',
                    trackingSubmodules: false]
            ],
            submoduleCfg: [],
            userRemoteConfigs: [[
                credentialsId: credentialsId,
                url: url
            ]]
        ])
    }
    checkoutCurrentConfigRevision(folder)
}

/**
 * Checks out this build's config commit in a compatibility repository's submodule.
 *
 * @param repository Compatibility repository folder containing the submodule
 */
def checkoutCurrentConfigRevision(String repository) {
    def currentCommit
    if (isUnix()) {
        currentCommit = sh(
            script: "git -C \"${env.WORKSPACE}\" rev-parse HEAD",
            returnStdout: true
        ).trim()
    } else {
        currentCommit = bat(
            script: '@echo off\r\ngit -C "%WORKSPACE%" rev-parse HEAD',
            returnStdout: true
        ).trim()
    }

    dir("${repository}/summit_drives_ci_configs") {
        if (isUnix()) {
            sh "git fetch --no-recurse-submodules \"${env.WORKSPACE}/.git\" ${currentCommit}"
            sh "git checkout --detach ${currentCommit}"
        } else {
            bat "git fetch --no-recurse-submodules \"${env.WORKSPACE}\\.git\" ${currentCommit}"
            bat "git checkout --detach ${currentCommit}"
        }
    }
}

def pipelineParams = PyTestParams.pytestParams(this, currentBuild, [
    excludeParameters: [
        'pythonVersions', // Single python version for Jenkins pipeline
    ],
    branchName: env.BRANCH_NAME,
    branchNameMaster: BRANCH_NAME_MASTER,
    runWithParametersConfig: [
        default: 'Set to last build',
    ],
    testSessionFilterConfig: [
        choices: [
            '.*',
            'ingm_.*',
            'ingw_.*',
        ],
    ],
    pytestLoggingLevelConfig: [
        default: DEFAULT_LOGGING_LEVEL,
    ],
    wiresharkLoggingConfig: [
        default: true,
    ],
])

properties([
    buildDiscarder(
        logRotator(
            daysToKeepStr: '90',
            numToKeepStr: '10',
            artifactDaysToKeepStr: '30',
            artifactNumToKeepStr: '5',
        )
    ),
    parameters(pipelineParams)
])

pipeline {
    agent none
    stages {
        stage('Inspect pipeline parameters') {
            steps {
                echo("${PyTestParams.configSummary(params, env, currentBuild)}")
            }
        }
        stage("Prepare Test Sessions") {
            agent {
                docker {
                    label 'lin-worker'
                    image LIN_DOCKER_IMAGE
                }
            }
            environment {
                VENV_WORKING_FOLDER = "${LIN_DOCKER_TMP_PATH}"
            }
            steps {
                script {
                    Set pythonVersions = [DEFAULT_PYTHON_VERSION] as Set

                    // Set dynamic properties on all test sessions
                    TEST_SESSIONS.setAttributeInCascade(
                        runInVirtualEnvs: venvManager.pythonVersionsToDefaultVenvNames(pythonVersions),
                        jobName: "${env.JOB_NAME}-#${env.BUILD_NUMBER}",
                        archiveData: "*",
                        testSelectionRepeatCount: PyTestParams.readValue(params, 'pytestRepeatCounts'),
                        logLevel: PyTestParams.readValue(params, 'pytestLoggingLevel')
                    )

                    def testSessionFilter = PyTestParams.readValue(params, 'testSessionFilter')
                    def testSessionSelection = PyTestParams.readValue(params, 'pytestSelection')
                    ingmTestManager.testSessionFilter = testSessionFilter
                    ingwTestManager.testSessionFilter = testSessionFilter
                    ingmTestManager.testSessionSelection = testSessionSelection
                    ingwTestManager.testSessionSelection = testSessionSelection

                    // Configure the compatibility tests - each one must run in its own poetry directory
                    // If the user is manually introducing a test selection, use that one, otherwise
                    // use the default selection for the compatibility tests.
                    def wiresharkLogging = PyTestParams.readValue(params, 'wiresharkLogging', env, currentBuild)
                    INGM_TEST_SESSIONS.setAttributeInCascade(
                        poetryBaseDir: 'ingeniamotion', // Run poetry commands from the ingeniamotion directory
                        testSessionSelection: ingmTestManager.testSessionSelection ?: 'tests/test_motion.py tests/test_feedbacks.py'
                    )
                    INGM_ECAT_TESTS.baseTestSession.setAttributeInCascade(useWiresharkLogging: wiresharkLogging)
                    INGM_ETH_TESTS.baseTestSession.setAttributeInCascade(useWiresharkLogging: wiresharkLogging)
                    INGM_SIRIUS_TESTS.baseTestSession.setAttributeInCascade(useWiresharkLogging: wiresharkLogging)
                    INGW_TEST_SESSIONS.setAttributeInCascade(
                        poetryBaseDir: 'ingeniawizard', // Run poetry commands from the ingeniawizard directory
                        testSessionSelection: ingwTestManager.testSessionSelection ?: 'tests/test_integration.py'
                    )
                    INGW_ECAT_TESTS.baseTestSession.setAttributeInCascade(useWiresharkLogging: wiresharkLogging)
                    INGW_SIRIUS_TESTS.baseTestSession.setAttributeInCascade(useWiresharkLogging: wiresharkLogging)


                    def runPolicyTags = [] as Set
                    if (PyTestParams.readValue(params, 'runPolicyNightly', env, currentBuild)) { runPolicyTags.add("nightly") }
                    if (PyTestParams.readValue(params, 'runPolicyWeekend', env, currentBuild)) { runPolicyTags.add("weekends") }
                    ingmTestManager.runPolicyTags = runPolicyTags
                    ingwTestManager.runPolicyTags = runPolicyTags

                }
            }
        }

        stage('Resolve Test Sessions') {
            agent {
                docker {
                    label 'lin-worker'
                    image LIN_DOCKER_IMAGE
                }
            }
            environment {
                VENV_WORKING_FOLDER = "${LIN_DOCKER_TMP_PATH}_resolve_sessions"
            }
            stages {
                stage ('Clean untracked files') {
                    steps {
                        script {
                            cleanUntrackedFiles()
                        }
                    }
                }
                stage('Checkout Repositories') {
                    parallel {
                        stage('Checkout Ingeniamotion Repository') {
                            steps {
                                script {
                                    checkoutRepository(
                                        'git@github.com:ingeniamc/ingeniamotion.git',
                                        INGM_COMPATIBILITY_BRANCH,
                                        'ingeniamotion',
                                        'GithubSSH'
                                    )
                                }
                            }
                        }
                        stage('Checkout Ingeniawizard Repository') {
                            steps {
                                script {
                                    checkoutRepository(
                                        'git@github.com:CeleraMotion/ingeniawizard.git',
                                        INGW_COMPATIBILITY_BRANCH,
                                        'ingeniawizard',
                                        'GithubSSH'
                                    )
                                }
                            }
                        }
                    }
                }
                stage('Move Workspace') {
                    steps {
                        script {
                            venvManager.copyToWorkingFolder()
                        }
                    }
                }
                stage('Build Test Sessions') {
                    steps {
                        script {
                            venvManager.createPoetryEnvironment()
                            withEnv(["PYTHONPATH=${LIN_DOCKER_TMP_PATH}_resolve_sessions/ingeniamotion"]) {
                                ingmTestManager.buildTestSessions("tests.setups.rack_specifiers")
                            }
                            withEnv(["PYTHONPATH=${LIN_DOCKER_TMP_PATH}_resolve_sessions/ingeniawizard"]) {
                                ingwTestManager.buildTestSessions("tests.setups.canopen_integration")
                                ingwTestManager.buildTestSessions("tests.setups.ethercat_integration")
                                ingwTestManager.buildTestSessions("tests.setups.sirius_integration")
                            }
                            venvManager.runInWorkingFolder("rm -rf .venv${DEFAULT_PYTHON_VERSION}")
                            echo("Test sessions configured:\n${TEST_SESSIONS.configSummary()}")
                        }
                    }
                }
                stage('Resolve Ingeniamotion Test Sessions') {
                    stages {
                        stage('Create virtual environments') {
                            steps {
                                script {
                                    venvManager.createPoetryEnvironments(
                                        pythonVersions: venvManager.defaultVenvNamesToVersion(INGM_ECAT_TESTS.baseTestSession.runInVirtualEnvs),
                                        installCommand: "poetry --directory ingeniamotion sync --all-groups --extras fsoe"
                                    )
                                }
                            }
                        }
                        stage('Resolve Test Sessions') {
                            steps {
                                script {
                                    ingmTestManager.resolveSession(INGM_ETH_TESTS)
                                    ingmTestManager.resolveSession(INGM_ECAT_TESTS)
                                    ingmTestManager.resolveSession(INGM_CAN_TESTS)
                                    ingmTestManager.resolveSession(INGM_SIRIUS_TESTS)
                                }
                            }
                        }
                    }
                }
                stage('Resolve Ingeniawizard Test Sessions') {
                    stages {
                        stage('Create virtual environments') {
                            steps {
                                script {
                                    venvManager.runInWorkingFolder("rm -rf .venv${DEFAULT_PYTHON_VERSION}")
                                    venvManager.createPoetryEnvironments(
                                        pythonVersions: venvManager.defaultVenvNamesToVersion(INGW_ECAT_TESTS.baseTestSession.runInVirtualEnvs),
                                        installCommand: "poetry --directory ingeniawizard sync --all-groups"
                                    )
                                }
                            }
                        }
                        stage('Resolve Test Sessions') {
                            steps {
                                script {
                                    ingwTestManager.resolveSession(INGW_ECAT_TESTS)
                                    ingwTestManager.resolveSession(INGW_CAN_TESTS)
                                    ingwTestManager.resolveSession(INGW_SIRIUS_TESTS)
                                }
                            }
                        }
                    }
                }
            }
        }

        stage('Compatibility Tests') {
            parallel {
                stage('EtherCAT - Tests') {
                    when {
                        beforeOptions true
                        beforeAgent true
                        expression {
                            INGM_ECAT_TESTS.anyShouldRun() || INGW_ECAT_TESTS.anyShouldRun()
                        }
                    }
                    options {
                        lock(ECAT_NODE_LOCK)
                    }
                    agent {
                        label ECAT_NODE
                    }
                    stages {
                        stage('Checkout Ingeniamotion Repository') {
                            when {
                                expression { INGM_ECAT_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Checkout') {
                                    steps {
                                        script {
                                            checkoutRepository(
                                                'git@github.com:ingeniamc/ingeniamotion.git',
                                                INGM_COMPATIBILITY_BRANCH,
                                                'ingeniamotion',
                                                'GithubSSH'
                                            )
                                        }
                                    }
                                }
                            }
                        }
                        stage('Checkout Ingeniawizard Repository') {
                            when {
                                expression { INGW_ECAT_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Checkout') {
                                    steps {
                                        script {
                                            checkoutRepository(
                                                'git@github.com:CeleraMotion/ingeniawizard.git',
                                                INGW_COMPATIBILITY_BRANCH,
                                                'ingeniawizard',
                                                'GithubSSH'
                                            )
                                        }
                                    }
                                }
                            }
                        }
                        stage('Ingeniamotion EtherCAT Tests') {
                            when {
                                expression { INGM_ECAT_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Create virtual environments') {
                                    steps {
                                        script {
                                            venvManager.runInWorkingFolder("if exist .venv${DEFAULT_PYTHON_VERSION} rmdir /s /q .venv${DEFAULT_PYTHON_VERSION}")
                                            venvManager.createPoetryEnvironments(
                                                pythonVersions: venvManager.defaultVenvNamesToVersion(INGM_ECAT_TESTS.baseTestSession.runInVirtualEnvs),
                                                installCommand: "poetry --directory ingeniamotion sync --all-groups --extras fsoe"
                                            )
                                        }
                                    }
                                }
                                stage('Run EtherCAT Tests') {
                                    steps {
                                        script {
                                            INGM_ECAT_TESTS.runTestStages()
                                        }
                                    }
                                }
                            }
                        }
                        stage('Ingeniawizard EtherCAT Tests') {
                            when {
                                expression { INGW_ECAT_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Create virtual environments') {
                                    steps {
                                        script {
                                            venvManager.runInWorkingFolder("if exist .venv${DEFAULT_PYTHON_VERSION} rmdir /s /q .venv${DEFAULT_PYTHON_VERSION}")
                                            venvManager.createPoetryEnvironments(
                                                pythonVersions: venvManager.defaultVenvNamesToVersion(INGW_ECAT_TESTS.baseTestSession.runInVirtualEnvs),
                                                installCommand: "poetry --directory ingeniawizard sync --all-groups"
                                            )
                                        }
                                    }
                                }
                                stage('Run EtherCAT Tests') {
                                    steps {
                                        script {
                                            INGW_ECAT_TESTS.runTestStages()
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                stage('CANopen/Ethernet - Tests') {
                    when {
                        beforeOptions true
                        beforeAgent true
                        expression {
                            INGM_CAN_TESTS.anyShouldRun() || INGM_ETH_TESTS.anyShouldRun() || INGW_CAN_TESTS.anyShouldRun()
                        }
                    }
                    options {
                        lock(CAN_NODE_LOCK)
                    }
                    agent {
                        label CAN_NODE
                    }
                    stages {
                        stage('Checkout Ingeniamotion Repository') {
                            when {
                                expression { INGM_CAN_TESTS.anyShouldRun() || INGM_ETH_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Checkout') {
                                    steps {
                                        script {
                                            checkoutRepository(
                                                'git@github.com:ingeniamc/ingeniamotion.git',
                                                INGM_COMPATIBILITY_BRANCH,
                                                'ingeniamotion',
                                                'GithubSSH'
                                            )
                                        }
                                    }
                                }
                            }
                        }
                        stage('Checkout Ingeniawizard Repository') {
                            when {
                                expression { INGW_CAN_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Checkout') {
                                    steps {
                                        script {
                                            checkoutRepository(
                                                'git@github.com:CeleraMotion/ingeniawizard.git',
                                                INGW_COMPATIBILITY_BRANCH,
                                                'ingeniawizard',
                                                'GithubSSH'
                                            )
                                        }
                                    }
                                }
                            }
                        }
                        stage('Ingeniamotion CANopen/Ethernet Tests') {
                            when {
                                expression { INGM_CAN_TESTS.anyShouldRun() || INGM_ETH_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Create virtual environments') {
                                    steps {
                                        script {
                                            venvManager.runInWorkingFolder("if exist .venv${DEFAULT_PYTHON_VERSION} rmdir /s /q .venv${DEFAULT_PYTHON_VERSION}")
                                            venvManager.createPoetryEnvironments(
                                                pythonVersions: venvManager.defaultVenvNamesToVersion(INGM_ETH_TESTS.baseTestSession.runInVirtualEnvs),
                                                installCommand: "poetry --directory ingeniamotion sync --all-groups --extras fsoe"
                                            )
                                        }
                                    }
                                }
                                stage('Run CANopen/Ethernet Tests') {
                                    steps {
                                        script {
                                            INGM_CAN_TESTS.runTestStages()
                                            INGM_ETH_TESTS.runTestStages()
                                        }
                                    }
                                }
                            }
                        }
                        stage('Ingeniawizard CANopen Tests') {
                            when {
                                expression { INGW_CAN_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Create virtual environments') {
                                    steps {
                                        script {
                                            venvManager.runInWorkingFolder("if exist .venv${DEFAULT_PYTHON_VERSION} rmdir /s /q .venv${DEFAULT_PYTHON_VERSION}")
                                            venvManager.createPoetryEnvironments(
                                                pythonVersions: venvManager.defaultVenvNamesToVersion(INGW_CAN_TESTS.baseTestSession.runInVirtualEnvs),
                                                installCommand: "poetry --directory ingeniawizard sync --all-groups"
                                            )
                                        }
                                    }
                                }
                                stage('Run CANopen Tests') {
                                    steps {
                                        script {
                                            INGW_CAN_TESTS.runTestStages()
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                stage('SIRIUS Integration Tests') {
                    when {
                        beforeOptions true
                        beforeAgent true
                        expression {
                            INGM_SIRIUS_TESTS.anyShouldRun() || INGW_SIRIUS_TESTS.anyShouldRun()
                        }
                    }
                    agent {
                        label SIRIUS_NODE
                    }
                    stages {
                        stage('Checkout Ingeniamotion Repository') {
                            when {
                                expression { INGM_SIRIUS_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Checkout') {
                                    steps {
                                        script {
                                            checkoutRepository(
                                                'git@github.com:ingeniamc/ingeniamotion.git',
                                                INGM_COMPATIBILITY_BRANCH,
                                                'ingeniamotion',
                                                'GithubSSH'
                                            )
                                        }
                                    }
                                }
                            }
                        }
                        stage('Checkout Ingeniawizard Repository') {
                            when {
                                expression { INGW_SIRIUS_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Checkout') {
                                    steps {
                                        script {
                                            checkoutRepository(
                                                'git@github.com:CeleraMotion/ingeniawizard.git',
                                                INGW_COMPATIBILITY_BRANCH,
                                                'ingeniawizard',
                                                'GithubSSH'
                                            )
                                        }
                                    }
                                }
                            }
                        }
                        stage('Ingeniamotion SIRIUS Tests') {
                            when {
                                expression { INGM_SIRIUS_TESTS.anyShouldRun() }
                            }
                            stages {
                                stage('Create virtual environment') {
                                    steps {
                                        script {
                                            venvManager.createPoetryEnvironments(
                                                pythonVersions: venvManager.defaultVenvNamesToVersion(INGM_SIRIUS_TESTS.baseTestSession.runInVirtualEnvs),
                                                installCommand: "poetry --directory ingeniamotion sync --all-groups --extras fsoe"
                                            )
                                        }
                                    }
                                }
                                stage('Run Tests') {
                                    steps {
                                        script {
                                            INGM_SIRIUS_TESTS.runTestStages()
                                        }
                                    }
                                }
                            }
                        }
                        stage('Create virtual environments') {
                            when {
                                expression { INGW_SIRIUS_TESTS.anyShouldRun() }
                            }
                            steps {
                                script {
                                    venvManager.runInWorkingFolder("rm -rf .venv${DEFAULT_PYTHON_VERSION}")
                                    venvManager.createPoetryEnvironments(
                                        pythonVersions: venvManager.defaultVenvNamesToVersion(INGW_SIRIUS_TESTS.baseTestSession.runInVirtualEnvs),
                                        installCommand: "poetry --directory ingeniawizard sync --all-groups"
                                    )
                                }
                            }
                        }
                        stage('Run Integration Tests') {
                            steps {
                                script {
                                    INGW_SIRIUS_TESTS.runTestStages()
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}