import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.XPath
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
import org.w3c.dom.Document

final var versionFile = file("version.properties")

final var cdkVersion = {
    var props = new Properties()
    versionFile.withInputStream(props::load)
    return props.getProperty('version', 'undefined')
}()


allprojects {
    version = cdkVersion
    apply plugin: 'java-library'
    apply plugin: 'maven-publish'

    group 'io.airbyte.bulk-cdk'

    // Disable spotbugs on test code, which gets annoying really quickly for @MicronautTest classes.
    spotbugsTest {
        enabled = false
    }

    dependencies {
        api platform('org.jetbrains.kotlin:kotlin-bom:2.0.10')
        api platform('org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1')
        api platform('com.fasterxml.jackson:jackson-bom:2.17.2')
        api platform('io.micronaut:micronaut-core-bom:4.6.1')
        api platform('org.junit:junit-bom:5.10.2')
        api platform('org.slf4j:slf4j-bom:2.0.13')
        api platform('org.apache.logging.log4j:log4j-bom:2.21.1')
        api platform('org.testcontainers:testcontainers-bom:1.20.1')

        api 'org.jetbrains.kotlin:kotlin-stdlib'
        api 'com.google.dagger:dagger:2.51.1'
        ksp 'com.google.dagger:dagger-compiler:2.52'

        annotationProcessor platform('io.micronaut:micronaut-core-bom:4.6.1')
        annotationProcessor 'info.picocli:picocli-codegen:4.7.6'
        annotationProcessor 'io.micronaut:micronaut-inject-kotlin'

        ksp platform('io.micronaut:micronaut-core-bom:4.6.1')
        ksp 'io.micronaut:micronaut-inject-kotlin'
        kspTest platform('io.micronaut:micronaut-core-bom:4.6.1')
        kspTest 'io.micronaut:micronaut-inject-kotlin'

        testImplementation("io.mockk:mockk:1.13.13")
    }

    publishing {
        publications {
            cdk(MavenPublication) {
                from components.java
            }
        }
        // This repository is only defined and used in the context of publishing artifacts
        // It's different from the 'airbyte-public-jars' defined in settings.gradle
        // only in its omission of the 'public' directory.
        // Any artifacts publish here will be available in the 'airbyte-public-jars' repo.
        repositories {
            maven {
                name 'airbyte-repo'
                url 'https://airbyte.mycloudrepo.io/repositories/airbyte-public-jars/'
                credentials {
                    username System.getenv('CLOUDREPO_USER')
                    password System.getenv('CLOUDREPO_PASSWORD')
                }
            }
        }
    }
}

tasks.register('checkBuildNumber') {
    description = "Check that the version doesn't exist"

    doLast {
        var repoUrl = "https://airbyte.mycloudrepo.io/public/repositories/airbyte-public-jars"
        var groupIdUrl = "${repoUrl}/io/airbyte/bulk-cdk"
        var artifactUrl = "${groupIdUrl}/bulk"
        var metadataXmlUrl = "${artifactUrl}/maven-metadata.xml"

        var connection = metadataXmlUrl.toURL().openConnection() as HttpURLConnection
        try {
            connection.setRequestMethod("GET")
            connection.setDoInput(true)
            var responseCode = connection.getResponseCode()
            if (responseCode != 200) {
                throw new GradleException("Unexpected HTTP response code ${responseCode} from ${metadataXmlUrl} : expected 200.")
            }
            InputStream responseContent = connection.inputStream
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
            DocumentBuilder builder = factory.newDocumentBuilder()
            Document doc = builder.parse(responseContent); // Replace with your XML file path

            doc.getDocumentElement().normalize()

            XPath xpath = XPathFactory.newInstance().newXPath()

            String expression = "/metadata/versioning/latest"

            String latestVersion = (String) xpath.compile(expression).evaluate(doc, XPathConstants.STRING)
            if (version.toString() == latestVersion) {
                throw new GradleException("Version already exist")
            }
        } finally {
            connection.disconnect()
        }
    }
}

tasks.register('bulkCdkBuild').configure {
    dependsOn allprojects.collect {it.tasks.named('build')}
}
tasks.register('bulkCdkPublish').configure {
    dependsOn allprojects.collect {it.tasks.named('publish')}
}

class BumpBulkCdkVersionTask extends DefaultTask {
    private boolean patch = false
    private boolean minor = false
    private boolean major = false
    private String changelogEntry = null
    private File versionFile
    private String currentVersion

    BumpBulkCdkVersionTask() {
        description = 'Bump the bulk CDK version number'
    }

    void setVersionFile(File versionFile) {
        this.versionFile = versionFile

        var props = new Properties()
        versionFile.withInputStream(props::load)
        currentVersion = props.getProperty('version', 'undefined')
    }

    @Option(option = "patch", description = "Bump patch version (x.y.Z)")
    void setPatch(boolean patch) {
        this.patch = patch
    }

    @Option(option = "minor", description = "Bump minor version (x.Y.z)")
    void setMinor(boolean minor) {
        this.minor = minor
    }

    @Option(option = "major", description = "Bump major version (X.y.z)")
    void setMajor(boolean major) {
        this.major = major
    }

    @Option(option = "changelog", description = "The changelog entry to populate")
    void setChangelogEntry(String changelogEntry) {
        this.changelogEntry = changelogEntry
    }

    @TaskAction
    void bumpVersion() {
        // Parse flags
        def bumpType = null
        def flagCount = [patch, minor, major].count { it }
        if (flagCount == 0) {
            throw new GradleException("Please specify version bump type: --patch, --minor, or --major")
        }
        if (flagCount > 1) {
            throw new GradleException("Please specify only one version bump type")
        }

        if (changelogEntry == null) {
            // Read changelog entry from stdin
            println "Enter changelog entry (press Ctrl+D when done):"
            def changelogEntryBuilder = new StringBuilder()
            System.in.withReader { reader ->
                def line
                while ((line = reader.readLine()) != null) {
                    changelogEntryBuilder.append(line).append('\n')
                }
            }
            if (changelogEntryBuilder.length() == 0) {
                throw new GradleException("Changelog entry cannot be empty")
            }
            changelogEntry = changelogEntryBuilder.toString()
        }

        // Compute new version number
        def versionParts = currentVersion.split('\\.')
        def majorNum = Integer.parseInt(versionParts[0])
        def minorNum = Integer.parseInt(versionParts[1])
        // TODO This will break on any `-whatever` suffix, but we're not currently doing that.
        //   If we ever want to get fancier with how we release the CDK, we should probably handle this better.
        def patchNum = Integer.parseInt(versionParts[2])
        def newVersion
        if (patch) {
            newVersion = "${majorNum}.${minorNum}.${patchNum + 1}"
        } else if (minor) {
            newVersion = "${majorNum}.${minorNum + 1}.0"
        } else if (major) {
            newVersion = "${majorNum + 1}.0.0"
        }

        println "Bumping version from ${currentVersion} to ${newVersion}"

        // Write out the new version number
        versionFile.text = "version=${newVersion}\n"

        // Update changelog file
        def changelogFile = project.file("changelog.md")
        def currentChangelog = changelogFile.text
        def newChangelog = new StringBuilder()
        // Add new version entry
        newChangelog.append("## Version ${newVersion}\n\n")
        newChangelog.append("${changelogEntry.toString().trim()}\n\n")
        // Add the rest of the existing changelog
        newChangelog.append(currentChangelog)
        // Write back to file
        changelogFile.text = newChangelog.toString()

        println "Version bumped to ${newVersion} and changelog updated"
    }
}

tasks.register('bumpVersion', BumpBulkCdkVersionTask) {
    it.versionFile = versionFile
}
