import static org.hamcrest.Matchers.allOf
import static org.hamcrest.Matchers.containsString

import org.hamcrest.Matchers
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain

import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException

import util.BasePiperTest
import util.JenkinsCredentialsRule
import util.JenkinsDockerExecuteRule
import util.JenkinsStepRule
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.Rules

import hudson.AbortException
import hudson.scm.NullSCM

public class TransportRequestReleaseTest extends BasePiperTest {

    private ExpectedException thrown = new ExpectedException()
    private JenkinsStepRule stepRule = new JenkinsStepRule(this)
    private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)

    @Rule
    public RuleChain ruleChain = Rules.getCommonRules(this)
        .around(new JenkinsReadYamlRule(this))
        .around(thrown)
        .around(stepRule)
        .around(loggingRule)
        .around(new JenkinsCredentialsRule(this)
            .withCredentials('CM', 'anonymous', '********'))

    @Before
    public void setup() {

        nullScript.commonPipelineEnvironment.configuration = [general:
                                     [changeManagement:
                                         [
                                          credentialsId: 'CM',
                                          type: 'SOLMAN',
                                          endpoint: 'https://example.org/cm'
                                         ]
                                     ]
                                 ]
    }

    @Test
    public void changeDocumentIdNotProvidedSOLMANTest() {

        ChangeManagement cm = new ChangeManagement(nullScript) {
            String getChangeDocumentId(String from,
                                       String to,
                                       String label,
                                       String format) {
                                throw new ChangeManagementException('Cannot retrieve change documentId')
            }
        }

        thrown.expect(IllegalArgumentException)
        thrown.expectMessage("Change document id not provided (parameter: 'changeDocumentId' or via commit history).")

        stepRule.step.transportRequestRelease(script: nullScript, transportRequestId: '001', cmUtils: cm)
    }

    @Test
    public void transportRequestIdNotProvidedTest() {

        ChangeManagement cm = new ChangeManagement(nullScript) {
            String getTransportRequestId(String from,
                                         String to,
                                         String label,
                                         String format) {
                throw new ChangeManagementException('Cannot retrieve transportRequestId')
            }
        }

        thrown.expect(IllegalArgumentException)
        thrown.expectMessage("Transport request id not provided (parameter: 'transportRequestId' or via commit history).")

        stepRule.step.transportRequestRelease(script: nullScript, changeDocumentId: '001', cmUtils: cm)
    }

    @Test
    public void releaseTransportRequestFailsSOLMANTest() {

        thrown.expect(AbortException)
        thrown.expectMessage("Something went wrong")

        ChangeManagement cm = new ChangeManagement(nullScript) {

            void releaseTransportRequestSOLMAN(
                                         Map docker,
                                         String changeId,
                                         String transportRequestId,
                                         String endpoint,
                                         String credentialsId,
                                         String clientOpts) {

                throw new ChangeManagementException('Something went wrong')
            }
        }

        stepRule.step.transportRequestRelease(script: nullScript, changeDocumentId: '001', transportRequestId: '001', cmUtils: cm)
    }

    @Test
    public void releaseTransportRequestFailsCTSTest() {

        thrown.expect(AbortException)
        thrown.expectMessage("Something went wrong")

        nullScript
            .commonPipelineEnvironment
                .configuration
                    .general
                        .changeManagement
                            .type = 'CTS'

        ChangeManagement cm = new ChangeManagement(nullScript) {

            void releaseTransportRequestCTS(
                                         Map docker,
                                         String transportRequestId,
                                         String endpoint,
                                         String credentialsId,
                                         String clientOpts) {

                throw new ChangeManagementException('Something went wrong')
            }
        }

        stepRule.step.transportRequestRelease(
            script: nullScript,
            transportRequestId: '001',
            cmUtils: cm)
    }

    @Test
    public void releaseTransportRequestSuccessRFCTest() {

        def receivedParameters

        nullScript
            .commonPipelineEnvironment
                .configuration
                    .general
                        .changeManagement =
                            [
                                credentialsId: 'CM',
                                type: 'RFC',
                                endpoint: 'https://example.org/rfc',
                                rfc: [
                                    dockerImage: 'rfc',
                                    dockerOptions: [],
                                ],
                            ]

        ChangeManagement cm = new ChangeManagement(nullScript) {
            void releaseTransportRequestRFC(
                Map docker,
                String transportRequestId,
                String endpoint,
                String developmentInstance,
                String developmentClient,
                String credentialsId,
                boolean verbose) {

                receivedParameters = [
                    docker: docker,
                    transportRequestId: transportRequestId,
                    endpoint: endpoint,
                    developmentInstance: developmentInstance,
                    developmentClient: developmentClient,
                    credentialsId: credentialsId,
                    verbose: verbose,
                ]
            }
        }

        stepRule.step.transportRequestRelease(
            script: nullScript,
            transportRequestId: '002',
            changeManagement: [
                rfc: [
                    developmentClient: '003',
                    developmentInstance: '002',
                ]
            ],
            verbose: true,
            cmUtils: cm)

        assert receivedParameters == [
                    docker: [
                        image: 'rfc',
                        options: [],
                        envVars: [:],
                        pullImage: true,
                    ],
                    transportRequestId: '002',
                    endpoint: 'https://example.org/rfc',
                    developmentInstance: '002',
                    developmentClient: '003',
                    credentialsId: 'CM',
                    'verbose': true,
                ]
    }

    @Test
    public void releaseTransportRequestSuccessCTSTest() {

        def receivedParameters

        nullScript
            .commonPipelineEnvironment
                .configuration
                    .general
                        .changeManagement =
                            [
                                credentialsId: 'CM',
                                type: 'CTS',
                                endpoint: 'https://example.org/cts'
                            ]

        ChangeManagement cm = new ChangeManagement(nullScript) {
            void releaseTransportRequestCTS(
                Map docker,
                String transportRequestId,
                String endpoint,
                String credentialsId,
                String clientOpts = '') {

                receivedParameters = [
                    docker: docker,
                    transportRequestId: transportRequestId,
                    endpoint: endpoint,
                    credentialsId: credentialsId,
                    clientOpts: clientOpts
                ]
            }
        }

        stepRule.step.transportRequestRelease(
            script: nullScript,
            transportRequestId: '002',
            cmUtils: cm)

        assert receivedParameters == [
                    docker: [
                        image:'ppiper/cm-client',
                        options:[],
                        envVars:[:],
                        pullImage:true,
                    ],
                    transportRequestId: '002',
                    endpoint: 'https://example.org/cts',
                    credentialsId: 'CM',
                    clientOpts: ''
                ]
    }

    @Test
    public void releaseTransportRequestFailsRFCTest() {

        thrown.expect(AbortException)
        thrown.expectMessage('Failed releasing transport request.')

        nullScript
            .commonPipelineEnvironment
                .configuration
                    .general
                        .changeManagement =
                            [
                                credentialsId: 'CM',
                                type: 'RFC',
                                endpoint: 'https://example.org/rfc',
                                rfc: [dockerImage: 'rfc']
                            ]

        ChangeManagement cm = new ChangeManagement(nullScript) {
            void releaseTransportRequestRFC(
                Map docker,
                String transportRequestId,
                String endpoint,
                String developmentInstance,
                String developmentClient,
                String credentialsId,
                boolean verbose) {

                throw new ChangeManagementException('Failed releasing transport request.')
            }
        }

        stepRule.step.transportRequestRelease(
            script: nullScript,
            transportRequestId: '002',
            changeManagement: [
                rfc: [
                    developmentClient: '003',
                    developmentInstance: '002'
                ]
            ],
            cmUtils: cm)

    }

    @Test
    public void releaseTransportRequestSanityChecksSOLMANTest() {

        thrown.expect(IllegalArgumentException)
        thrown.expectMessage(allOf(
            containsString('ERROR - NO VALUE AVAILABLE FOR'),
            containsString('changeManagement/endpoint')))

        // changeDocumentId and transportRequestId are not checked
        // by the sanity checks here since they are looked up from
        // commit history in case they are not provided.

        nullScript
            .commonPipelineEnvironment
                .configuration = null

        stepRule.step.transportRequestRelease(
            script: nullScript,
            changeManagement: [type: 'SOLMAN']
        )
    }

    @Test
    public void releaseTransportRequestSanityChecksCTSTest() {

        thrown.expect(IllegalArgumentException)
        thrown.expectMessage(allOf(
            containsString('ERROR - NO VALUE AVAILABLE FOR'),
            containsString('changeManagement/endpoint')))

        nullScript
            .commonPipelineEnvironment
                .configuration = null

        stepRule.step.transportRequestRelease(
            script: nullScript,
            changeManagement: [type: 'CTS']
        )
    }

    @Test
    public void releaseTransportRequestSanityChecksRFCTest() {

        thrown.expect(IllegalArgumentException)
        thrown.expectMessage(allOf(
            containsString('ERROR - NO VALUE AVAILABLE FOR:'),
            containsString('changeManagement/endpoint'),
            containsString('developmentClient')))

        nullScript
            .commonPipelineEnvironment
                .configuration = null

        stepRule.step.transportRequestRelease(
            script: nullScript,
            changeManagement: [type: 'RFC'],
            transportRequestId: '002')
    }

    @Test
    public void releaseTransportRequestSuccessSOLMANTest() {

        // Here we test only the case where the transportRequestId is
        // provided via parameters. The other cases are tested by
        // corresponding tests for StepHelpers#getTransportRequestId(./.)

        loggingRule.expect("[INFO] Closing transport request '002' for change document '001'.")
        loggingRule.expect("[INFO] Transport Request '002' has been successfully closed.")

        Map receivedParams = [:]

        ChangeManagement cm = new ChangeManagement(nullScript) {
            void releaseTransportRequestSOLMAN(
                                         Map docker,
                                         String changeId,
                                         String transportRequestId,
                                         String endpoint,
                                         String credentialsId,
                                         String clientOpts) {

                receivedParams.docker = docker
                receivedParams.changeId = changeId
                receivedParams.transportRequestId = transportRequestId
                receivedParams.endpoint = endpoint
                receivedParams.credentialsId = credentialsId
                receivedParams.clientOpts = clientOpts
            }
        }

        stepRule.step.transportRequestRelease(script: nullScript, changeDocumentId: '001', transportRequestId: '002', cmUtils: cm)

        assert receivedParams == [
                                  docker: [
                                      image: 'ppiper/cm-client',
                                      pullImage: true,
                                      envVars: [:],
                                      options: [],
                                  ],
                                  changeId: '001',
                                  transportRequestId: '002',
                                  endpoint: 'https://example.org/cm',
                                  credentialsId: 'CM',
                                  clientOpts: '']
    }

    @Test
    public void cmIntegrationSwichtedOffTest() {

        loggingRule.expect('[INFO] Change management integration intentionally switched off.')

        stepRule.step.transportRequestRelease(script: nullScript,
            changeManagement: [type: 'NONE'])
    }
}
