package twitch.instrumentorum.project.builds

import jetbrains.buildServer.configs.kotlin.v2018_2.ErrorConsumer
import jetbrains.buildServer.configs.kotlin.v2018_2.TeamCityDsl
import jetbrains.buildServer.configs.kotlin.v2018_2.buildFeatures.dockerSupport
import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.DockerComposeStep
import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.ExecBuildStep
import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.ScriptBuildStep
import twitch.instrumentorum.BuildGroup
import twitch.instrumentorum.InstrumBuild
import twitch.instrumentorum.InstrumProject
import twitch.instrumentorum.enums.AWSRegion
import twitch.instrumentorum.enums.ProjectSetting

@TeamCityDsl
open class DockerAwareBuild(name: String, buildGroup: BuildGroup) : InstrumBuild(name, buildGroup) {
    var cleanupImages: Boolean = true
    var ecrAccountId: String = ""
    var ecrAssumeRole: String = ""
    var ecrRegion: AWSRegion = AWSRegion.PDX

    val ecrDomain: String
        get() = "$ecrAccountId.dkr.ecr.${ecrRegion.region}.amazonaws.com"

    constructor(name: String, buildGroup: BuildGroup, init: DockerAwareBuild.() -> Unit) : this(name, buildGroup) {
        init()
    }

    override fun finalize() {
        super.finalize()

        ecrAccountId = project.getGlobal(ProjectSetting.EcrAccountId) ?: "994136867826"
        ecrAssumeRole = project.getGlobal(ProjectSetting.EcrAssumeRole) ?: ""

        textParam("ecr.domain", ecrDomain, "ECR Domain")

        val connection = rootProject.ecrConnection(ecrAccountId, ecrRegion, ecrAssumeRole)
        features.dockerSupport {
            cleanupPushedImages = this@DockerAwareBuild.cleanupImages
            loginToRegistry = on {
                dockerRegistryId = connection.id
            }
        }

        // Typings force me to duplicate this code...
        // Wish I could fake an interface, I could delete one of these blocks...
        steps.items.forEach { step ->
            if (step is ScriptBuildStep && !step.dockerImage.isNullOrBlank()) {
                finalizeScriptStep(step)
            }

            if (step is ExecBuildStep && !step.dockerImage.isNullOrBlank()) {
                finalizeExecStep(step)
            }

            if (step is DockerComposeStep && step.forcePull == null) {
                step.forcePull = true
            }
        }
    }

    internal fun finalizeExecStep(step: ExecBuildStep) {
        if (step.dockerPull == null) {
            step.dockerPull = true
        }

        if (step.dockerRunParameters?.contains("-e TEAMCITY_VERSION") != true) {
            step.dockerRunParameters = "-e TEAMCITY_VERSION ${step.dockerRunParameters ?: ""}".trim()
        }

        if (step.dockerImagePlatform == ExecBuildStep.ImagePlatform.Any) {
            step.dockerImagePlatform = ExecBuildStep.ImagePlatform.Linux
        }
    }

    internal fun finalizeScriptStep(step: ScriptBuildStep) {
        if (step.dockerPull == null) {
            step.dockerPull = true
        }

        if (step.dockerRunParameters?.contains("-e TEAMCITY_VERSION") != true) {
            step.dockerRunParameters = "-e TEAMCITY_VERSION ${step.dockerRunParameters ?: ""}".trim()
        }

        if (step.dockerImagePlatform == ScriptBuildStep.ImagePlatform.Any) {
            step.dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
        }
    }

    override fun validate(consumer: ErrorConsumer) {
        super.validate(consumer)

        if (!ecrAccountId.matches("^[0-9]{12}$".toRegex())) {
            val message = "mandatory 'ecrAccountId' property must be a 12 digit numerical value"
            consumer.consumePropertyError("ecrAccountId", message)
        }
    }
}

fun BuildGroup.dockerAwareBuild(buildName: String, init: DockerAwareBuild.() -> Unit): InstrumBuild =
    DockerAwareBuild(buildName, this, init)

fun InstrumProject.dockerAwareBuild(buildName: String, init: DockerAwareBuild.() -> Unit): InstrumBuild =
    BuildGroup(this, false).dockerAwareBuild(buildName, init)
