/*
 * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

plugins {
    id "com.github.spotbugs-base" version "${spotbugsPluginVersion}"
    id "com.github.johnrengelman.shadow" version "${shadowJarPluginVersion}"
    id "de.undercouch.download" version "${downloadPluginVersion}"
    id "net.researchgate.release" version "${releasePluginVersion}"
}
apply plugin: 'maven-publish'


allprojects {
    group = project.group
    version = project.version

    apply plugin: 'jacoco'
    apply plugin: 'maven-publish'

    repositories {
        mavenLocal()
        maven {
            url = 'https://maven.wso2.org/nexus/content/repositories/releases/'
        }

        maven {
            url = 'https://maven.wso2.org/nexus/content/groups/wso2-public/'
        }

        maven {
            url = 'https://repo.maven.apache.org/maven2'
        }

        maven {
            url = 'https://maven.pkg.github.com/ballerina-platform/*'
            credentials {
                username System.getenv("packageUser")
                password System.getenv("packagePAT")
            }
        }
    }

    ext {
        snapshotVersion= '-SNAPSHOT'
        timestampedVersionRegex = '.*-\\d{8}-\\d{6}-\\w.*\$'
    }
}

subprojects {
    apply plugin: "java"
    configurations {
        ballerinaStdLibs
    }
    dependencies {
        /* Standard libraries */
        ballerinaStdLibs "io.ballerina.stdlib:constraint-ballerina:${stdlibConstraintVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:io-ballerina:${stdlibIoVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:random-ballerina:${stdlibRandomVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:time-ballerina:${stdlibTimeVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:url-ballerina:${stdlibUrlVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:crypto-ballerina:${stdlibCryptoVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:log-ballerina:${stdlibLogVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:os-ballerina:${stdlibOsVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:uuid-ballerina:${stdlibUuidVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:auth-ballerina:${stdlibAuthVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:oauth2-ballerina:${stdlibOAuth2Version}"
        ballerinaStdLibs "io.ballerina.lib:data.jsondata-ballerina:${stdlibJsonDataVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:http-ballerina:${stdlibHttpVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:graphql-ballerina:${stdlibGraphqlVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:grpc-ballerina:${stdlibGrpcVersion}"
        ballerinaStdLibs "io.ballerina:observe-ballerina:${observeInternalVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:observe-ballerina:${observeVersion}"
        ballerinaStdLibs "io.ballerina.stdlib:tcp-ballerina:${stdlibTcpVersion}"

        /* Ballerina Persist Tool */
        ballerinaStdLibs "io.ballerina:persist-tools:${persistToolVersion}"
    }

    ext.defineSyncTask = { String pathParam ->
        tasks.register('localSync') {
            doLast {
                def distVersion = ""
                def distributionsDir = new File("${System.getProperty('user.home')}/.ballerina/ballerina-home/distributions/")
                if (distributionsDir.exists()) {
                    def ballerinaDirectories = distributionsDir.listFiles().findAll { 
                        it.isDirectory() && it.name.startsWith("ballerina-") 
                    }
                    
                    if (ballerinaDirectories) {
                        ballerinaDirectories.sort { a, b -> b.name <=> a.name }
                        def latestDir = ballerinaDirectories[0]
                        distVersion = latestDir.name.substring("ballerina-".length())
                    } else {
                        println "No Ballerina distributions found."
                        return
                    }
                } else {
                    println "Distributions directory does not exist at: ${distributionsDir.absolutePath}"
                    return
                }

                def buildLibsDir = file('build/libs')
                def destinationPath = "${System.getProperty('user.home')}/.ballerina/ballerina-home/distributions/ballerina-${distVersion}/${pathParam}"

                if (buildLibsDir.exists() && buildLibsDir.listFiles()) {
                    def jarFiles = buildLibsDir.listFiles().findAll { it.name.endsWith('.jar') }
                    if (jarFiles) {
                        // Get module name from the built jar (removing version part)
                        def jarName = jarFiles[0].name
                        def moduleJarName = jarName.contains('-') ? jarName.substring(0, jarName.lastIndexOf('-')) : jarName.replace('.jar', '')
                        
                        def jarsToRemove = fileTree(destinationPath).matching {
                            include "${moduleJarName}*.jar"
                        }
                        if (!jarsToRemove.isEmpty()) {
                            jarsToRemove.each { jar ->
                                println "Removing existing jar: ${jar}"
                                jar.delete()
                            }
                        }
                    }
                }
                copy {
                    from fileTree(dir: 'build/libs', include: '*.jar')
                    println "Updating ${destinationPath}"
                    into destinationPath
                }
             }
        }
    }
}

def moduleVersion = project.version
if (moduleVersion.indexOf('-') != -1) {
    moduleVersion = moduleVersion.substring(0, moduleVersion.indexOf('-'))
}
def artifactBreLib = file("$project.rootDir/build/ls-extensions/bre-libs")
def artifactLsLib = file("$project.rootDir/build/ls-extensions/ls-libs")
def artifactLibParent = file("$project.rootDir/build/architecture-model-generator/")
def targetModelGeneratorCommons = file("$project.rootDir/model-generator-commons/build/libs/model-generator-commons-${project.version}.jar")
def targetArchModelGeneratorCore = file("$project.rootDir/architecture-model-generator/modules/architecture-model-generator-core/build/libs/architecture-model-generator-core-${project.version}.jar")
def targetArchModelGeneratorPlugin = file("$project.rootDir/architecture-model-generator/modules/architecture-model-generator-plugin/build/libs/architecture-model-generator-plugin-${project.version}.jar")
def targetArchModelGeneratorLSExt = file("$project.rootDir/architecture-model-generator/modules/architecture-model-generator-ls-extension/build/libs/architecture-model-generator-ls-extension-${project.version}.jar")
def targetGraphqlModelGeneratorCore = file("$project.rootDir/graphql-model-generator/modules/graphql-model-generator-core/build/libs/graphql-model-generator-core-${project.version}.jar")
def targetGraphqlModelGeneratorLSExt = file("$project.rootDir/graphql-model-generator/modules/graphql-model-generator-ls-extension/build/libs/graphql-model-generator-ls-extension-${project.version}.jar")
def targetFlowModelGeneratorCore = file("$project.rootDir/flow-model-generator/modules/flow-model-generator-core/build/libs/flow-model-generator-core-${project.version}.jar")
def targetFlowModelGeneratorLSExt = file("$project.rootDir/flow-model-generator/modules/flow-model-generator-ls-extension/build/libs/flow-model-generator-ls-extension-${project.version}.jar")
def targetFlowModelGeneratorCentral = file("$project.rootDir/flow-model-generator/modules/flow-model-central-client/build/libs/flow-model-central-client-${project.version}.jar")
def targetServiceModelGeneratorLSExt = file("$project.rootDir/service-model-generator/modules/service-model-generator-ls-extension/build/libs/service-model-generator-ls-extension-${project.version}.jar")
def targetSequenceDiagramGeneratorCore = file("$project.rootDir/sequence-model-generator/modules/sequence-model-generator-core/build/libs/sequence-model-generator-core-${project.version}.jar")
def targetSequenceDiagramGeneratorLSExt = file("$project.rootDir/sequence-model-generator/modules/sequence-model-generator-ls-extension/build/libs/sequence-model-generator-ls-extension-${project.version}.jar")
def targetTestManagerServiceLSExt = file("$project.rootDir/test-manager-service/modules/test-manager-service-ls-extension/build/libs/test-manager-service-ls-extension-${project.version}.jar")

// TODO: Remove this once the workspace manager is refactored to import modules where necessary.
def pullBallerinaModule(String packageName, String version = null, String orgName = "ballerinax") {
    tasks.register("pullBallerinaModule_${packageName.replace('/', '_')}") {
        doLast {
            def errOutput = new ByteArrayOutputStream()
            def result = exec {
                ignoreExitValue = true

                // Check if the package exists in the ballerina user home
                def centralRepoDir = new File(System.getProperty("user.home"), ".ballerina/repositories/central.ballerina.io/bala/${orgName}")
                if (centralRepoDir.exists()) {
                    def pkgDir = new File(centralRepoDir, packageName)
                    if (pkgDir.exists()) {
                        commandLine 'echo', "Package ${packageName} exists"
                        return
                    }
                }

                def balPullCommand = "bal pull ${orgName}/${packageName}${version ? ':' + version : ''} --sticky=false"
                if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
                    commandLine 'cmd', '/c', balPullCommand
                } else {
                    commandLine '/bin/sh', '-c', balPullCommand
                }
                errorOutput = errOutput
            }
            if (result.exitValue != 0) {
                println errOutput
                throw new GradleException("Failed to pull Ballerina module: ${packageName}")
            }
        }
    }
}

task copyArtifactZip {
    dependsOn(":model-generator-commons:build")
    dependsOn(":architecture-model-generator:architecture-model-generator-core:build")
    dependsOn(":architecture-model-generator:architecture-model-generator-plugin:build")
    dependsOn(":architecture-model-generator:architecture-model-generator-ls-extension:build")
    dependsOn(":graphql-model-generator:graphql-model-generator-core:build")
    dependsOn(":graphql-model-generator:graphql-model-generator-ls-extension:build")
    dependsOn(":generatePomFileForMavenJavaPublication")
    dependsOn(":flow-model-generator:flow-model-central-client:build")
    dependsOn(":flow-model-generator:flow-model-generator-core:build")
    dependsOn(":flow-model-generator:flow-model-generator-ls-extension:build")
    dependsOn(":service-model-generator:service-model-generator-ls-extension:build")
    dependsOn(":test-manager-service:test-manager-service-ls-extension:build")
    dependsOn(":sequence-model-generator:sequence-model-generator-core:build")
    dependsOn(":sequence-model-generator:sequence-model-generator-ls-extension:build")
    dependsOn(":test-manager-service:test-manager-service-ls-extension:build")
    inputs.dir file(project.rootDir)
    doLast {
        def breLibArtifacts = [
            targetModelGeneratorCommons,
            targetArchModelGeneratorCore,
            targetArchModelGeneratorPlugin,
            targetGraphqlModelGeneratorCore,
            targetSequenceDiagramGeneratorCore,
            targetFlowModelGeneratorCore,
            targetFlowModelGeneratorCentral
        ]

        def lsLibArtifacts = [
            targetArchModelGeneratorLSExt,
            targetGraphqlModelGeneratorLSExt,
            targetSequenceDiagramGeneratorLSExt,
            targetFlowModelGeneratorLSExt,
            targetServiceModelGeneratorLSExt,
            targetTestManagerServiceLSExt
        ]

        breLibArtifacts.each { artifact ->
            copy {
                from artifact
                into file(artifactBreLib)
            }
        }

        lsLibArtifacts.each { artifact ->
            copy {
                from artifact
                into file(artifactLsLib)
            }
        }
    }
    outputs.dir artifactLibParent
    doNotTrackState("build needs to run every time")
}

task createArtifactZip(type: Zip) {
    dependsOn copyArtifactZip
    dependsOn(":testerina-report-tools:createTesterinaReportToolsZip")
    from "build"
    archiveFileName = 'ballerina-dev-tools-' + project.version + '.zip'
    destinationDirectory = file("dist")
    doNotTrackState("build needs to run every time")
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            artifact source: createArtifactZip, extension: 'zip'
        }
    }

    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/ballerina-platform/ballerina-dev-tools")
            credentials {
                username = System.getenv("packageUser")
                password = System.getenv("packagePAT")
            }
        }
    }
}

release {
    failOnPublishNeeded = false

    buildTasks = ['build']
    versionPropertyFile = 'gradle.properties'
    tagTemplate = 'v${version}'
    git {
        // To release from any branch
        requireBranch = "release-${moduleVersion}"
        pushToRemote = 'origin'
    }
}

task test {
    dependsOn(":graphql-model-generator:graphql-model-generator-ls-extension:test")
}

test.dependsOn rootProject.pullBallerinaModule('ai', ballerinaxAiAgent)
test.dependsOn rootProject.pullBallerinaModule('np', ballerinaxNp)
test.dependsOn rootProject.pullBallerinaModule('kafka', ballerinaxKafkaVersion)
test.dependsOn rootProject.pullBallerinaModule('rabbitmq', ballerinaxRabbitmqVersion)
test.dependsOn rootProject.pullBallerinaModule('trigger.github', ballerinaxGithubTriggerVersion)

task build {
    dependsOn test
    dependsOn createArtifactZip
}

task clean {
    delete "build"
    delete "dist"
}

task buildFlow {
    dependsOn(":flow-model-generator:flow-model-generator-core:build")
    dependsOn(":flow-model-generator:flow-model-generator-ls-extension:build")
}

task buildService {
    dependsOn(":service-model-generator:service-model-generator-ls-extension:build")
}

task buildDesign {
    dependsOn(":architecture-model-generator:architecture-model-generator-core:build")
    dependsOn(":architecture-model-generator:architecture-model-generator-ls-extension:build")
}

task buildSequence {
    dependsOn(":sequence-model-generator:sequence-model-generator-core:build")
    dependsOn(":sequence-model-generator:sequence-model-generator-ls-extension:build")
}

task buildTestService {
    dependsOn(":test-manager-service:test-manager-service-ls-extension:build")
}

task buildCommons {
    dependsOn(":model-generator-commons:build")
}

build.doNotTrackState("build needs to run every time")
publishToMavenLocal.dependsOn build
publish.dependsOn build
