import groovy.json.*
import java.util.*
import com.cloudbees.groovy.cps.NonCPS

@Library("dbz-libs") _

DRY_RUN = common.getDryRun()

IMAGES_DIR = 'images'
TAG_REST_ENDPOINT = "https://api.github.com/repos/${params.DEBEZIUM_REPOSITORY}/tags"
STREAMS_TO_BUILD_COUNT = params.STREAMS_TO_BUILD_COUNT.toInteger()
TAGS_PER_STREAM_COUNT = params.TAGS_PER_STREAM_COUNT.toInteger()
GIT_CREDENTIALS_ID = 'debezium-github'
DOCKER_CREDENTIALS_ID = 'debezium-dockerhub'
QUAYIO_CREDENTIALS_ID = 'debezium-quay'

class Version implements Comparable {
    int major
    int minor
    int micro
    String classifier

    def Version(major, minor) {
        this.major = major
        this.minor = minor
        this.micro = -1
    }

    Version(name) {
        def parts = name.split('\\.')
        major = Integer.parseInt(parts[0])
        minor = Integer.parseInt(parts[1])
        micro = Integer.parseInt(parts[2])
        classifier = parts[3]
    }

    @NonCPS
    def majorMinor() {
        new Version(major, minor)
    }

    @Override
    @NonCPS
    def String toString() {
        def sb = new StringBuilder()
        if (major != -1) {
            sb.append(major)
        }
        if (minor != -1) {
            sb.append('.')
            sb.append(minor)
        }
        if (micro != -1) {
            sb.append('.')
            sb.append(micro)
        }
        if (classifier != null) {
            sb.append('.')
            sb.append(classifier)
        }
        sb.toString()
    }

    @Override
    @NonCPS
    boolean equals(o) {
        (o == null) ? false : toString() == o.toString()
    }

    @Override
    @NonCPS
    int hashCode() {
        toString().hashCode()
    }

    @Override
    @NonCPS
    int compareTo(o) {
        int d = major - o.major
        if (d != 0) {
            return d
        }
        d = minor - o.minor
        if (d != 0) {
            return d
        }
        d = micro - o.micro
        if (d != 0) {
            return d
        }
        if (classifier != null) {
            return classifier.compareTo(o.classifier)
        }
        return 0
    }
}

// Map keyed by stream - e.g 1.5 and containing a list of tags per-stream
versions = [:] as TreeMap

// The most recent streams to be built
streamsToBuild = null

// The most resent stable stream representing latest tag
stableStream = null

@NonCPS
def initVersions(username, password) {
    TAG_REST_ENDPOINT.toURL().openConnection().with {
        doOutput = true
        setRequestProperty('Content-Type', 'application/json')
        setRequestProperty('Authorization', 'Basic ' + Base64.encoder.encodeToString("$username:$password".getBytes(java.nio.charset.StandardCharsets.UTF_8)))
        def json = new JsonSlurper().parse(new StringReader(content.text))
        json.each {
            def current = new Version(it.name.substring(1))
            def stream = current.majorMinor()
            versionList = versions[stream]
            if (versionList == null) {
                versionList = []
                versions[stream] = versionList
            }
            versionList.add(current)
        }
    }

    echo "Versions: $versions"
    // Order the tags per stream form the most recent one
    versions.each { k, v ->
        Collections.sort(v)
        Collections.reverse(v)
    }

    // Find the most recent streams and the latest stable
    streamsToBuild = [versions.lastKey()]
    for (int i = 1; i < STREAMS_TO_BUILD_COUNT; i++) {
        streamsToBuild.add(versions.lowerKey(streamsToBuild[-1]))
    }
    stableStream = streamsToBuild.find {
        def tags = versions[it]
        tags.size() > 0 && tags[0].classifier == 'Final'
    }
}

node('Slave') {
    catchError {
        timeout(time: 12, unit: 'HOURS') {
            stage('Initialize') {
                dir('.') {
                    deleteDir()
                }
                checkout([$class                           : 'GitSCM',
                        branches                         : [[name: params.IMAGES_BRANCH]],
                        doGenerateSubmoduleConfigurations: false,
                        extensions                       : [[$class: 'RelativeTargetDirectory', relativeTargetDir: IMAGES_DIR]],
                        submoduleCfg                     : [],
                        userRemoteConfigs                : [[url: "https://$params.IMAGES_REPOSITORY", credentialsId: GIT_CREDENTIALS_ID]]
                ]
                )
                withCredentials([usernamePassword(credentialsId: GIT_CREDENTIALS_ID, passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
                    initVersions(GIT_USERNAME, GIT_PASSWORD)
                }
                withCredentials([usernamePassword(credentialsId: DOCKER_CREDENTIALS_ID, passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) {
                    sh """
                        docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
                    """
                }
                withCredentials([string(credentialsId: QUAYIO_CREDENTIALS_ID, variable: 'USERNAME_PASSWORD')]) {
                    def credentials = USERNAME_PASSWORD.split(':')
                    sh """
                        set +x
                        docker login -u ${credentials[0]} -p ${credentials[1]} quay.io
                    """
                }
                dir(IMAGES_DIR) {
                    sh """
                    ./setup-local-builder.sh
                    docker run --privileged --rm mirror.gcr.io/tonistiigi/binfmt --install all
                    """
                }
                sh ""
            }
            stage('master') {
                echo "Building images for streams $streamsToBuild"
                dir(IMAGES_DIR) {
                    sh """
                    DRY_RUN=$DRY_RUN DEBEZIUM_VERSIONS=\"${streamsToBuild.join(' ')}\" LATEST_STREAM=\"$stableStream\" PLATFORM_CONDUCTOR_PLATFORM=linux/amd64 PLATFORM_STAGE_PLATFORM=linux/amd64 ./build-all-multiplatform.sh
                    """
                }
            }
            for (stream in streamsToBuild) {
                for (tag in versions[stream].take(TAGS_PER_STREAM_COUNT)) {
                    echo "Building images for tag $tag"
                    stage(tag.toString()) {
                        dir(IMAGES_DIR) {
                            // Disable IPv6 as Node.js has problems downloading dependencies using it
                            sh """
                                sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
                                sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
                            """
                            sh """
                                git checkout v$tag
                                git fetch origin $IMAGES_BRANCH:$IMAGES_BRANCH
                                git checkout $IMAGES_BRANCH build-all-multiplatform.sh build-debezium-multiplatform.sh build-postgres-multiplatform.sh script-functions/
                                echo '========== Building UI only for linux/amd64, arm64 not working =========='
                                DRY_RUN=$DRY_RUN PLATFORM_CONDUCTOR_PLATFORM=linux/amd64 PLATFORM_STAGE_PLATFORM=linux/amd64 RELEASE_TAG=$tag ./build-debezium-multiplatform.sh $stream $MULTIPLATFORM_PLATFORMS
                                git reset --hard
                            """
                        }
                    }
                }
            }
        }
    }

    mail to: params.MAIL_TO, subject: "${env.JOB_NAME} run #${env.BUILD_NUMBER} finished with ${currentBuild.currentResult}", body: "Run ${env.BUILD_URL} finished with result: ${currentBuild.currentResult}"
}
