import org.jetbrains.changelog.Changelog
import org.jetbrains.changelog.markdownToHTML
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile


val pluginNameProp = prop("pluginName")
val pluginVersionProp = prop("pluginVersion")
val pluginGroupProp = prop("pluginGroup")
val riderPlatformVersionProp = prop("riderPlatformVersion")
val ideaPlatformVersionProp = prop("ideaPlatformVersion")
val pluginSinceBuildProp = prop("pluginSinceBuild")
val pluginUntilBuildProp = prop("pluginUntilBuild")
val projectType = System.getenv("IDE_TYPE") ?: "IC"


plugins {
    idea
    kotlin("jvm") version "1.9.0"
    // Gradle IntelliJ Plugin
    id("org.jetbrains.intellij") version "1.16.1"
    // Gradle Changelog Plugin
    id("org.jetbrains.changelog") version "2.0.0"
}


allprojects {
    group = pluginGroupProp
    version = pluginVersionProp

    apply {
        plugin("idea")
        plugin("kotlin")
        plugin("org.jetbrains.intellij")
        plugin("org.jetbrains.changelog")
    }

    repositories {
        mavenCentral()
    }

    kotlin {
        jvmToolchain {
            languageVersion.set(JavaLanguageVersion.of(17))
        }
    }

    intellij {
        if (projectType == "RD") {
            version.set("RD-$riderPlatformVersionProp")
        } else {
            version.set("IC-$ideaPlatformVersionProp")
        }
    }

    tasks {
        withType<KotlinCompile> {
            kotlinOptions {
                jvmTarget = "17"
            }
        }

        runIde { enabled = false }
        prepareSandbox { enabled = false }
        buildSearchableOptions { enabled = false }
    }
}


// Main plugin project
project(":plugin") {
    group = pluginGroupProp
    version = pluginVersionProp

    intellij {
        pluginName.set(pluginNameProp)
        type.set(System.getenv("IDE_TYPE") ?: "IC")
    }

    dependencies {
        implementation(project(":common"))
        implementation(project(":idea"))
        implementation(project(":rider"))
    }

    changelog {
        version.set(pluginVersionProp)
        groups.set(emptyList())
    }

    // Task that collects all JARs from dependencies and merges them into a single JAR file
    val mergePluginJarTask = task<Jar>("mergePluginJars") {
        duplicatesStrategy = DuplicatesStrategy.FAIL
        archiveBaseName.set(pluginNameProp)

        exclude("META-INF/MANIFEST.MF")
        exclude("**/classpath.index")

        val pluginLibDir by lazy {
            val sandboxTask = tasks.prepareSandbox.get()
            sandboxTask.destinationDir.resolve("${sandboxTask.pluginName.get()}/lib")
        }

        val pluginJars by lazy {
            pluginLibDir.listFiles().orEmpty().filter { it.isPluginJar() }
        }

        destinationDirectory.set(project.layout.dir(provider { pluginLibDir }))

        doFirst {
            for (file in pluginJars) {
                from(zipTree(file))
            }
        }

        doLast {
            delete(pluginJars)
        }
    }

    tasks {
        buildPlugin {
            archiveBaseName.set(pluginNameProp)
        }
        runIde {
            enabled = true
            // Set the IDE to run (IntelliJ IDEA or JetBrains Rider)
            // Take value from and environment variable or use IntelliJ IDEA by default
        }
        prepareSandbox {
            finalizedBy(mergePluginJarTask)
            enabled = true
            doLast {
                copy {
                    from(file("${projectDir.parent}/rider/dotnet"))
                    into(file("$buildDir/idea-sandbox/plugins/${rootProject.name}/dotnet/Extensions/$pluginGroupProp/settings"))
                    include("templates.DotSettings")
                }
            }
        }
        buildSearchableOptions {
            dependsOn(mergePluginJarTask)
            enabled = true
        }
        runIde {
            maxHeapSize = "1500m"
        }

        patchPluginXml {
            version.set(pluginVersionProp)
            sinceBuild.set(pluginSinceBuildProp)
            if (pluginUntilBuildProp.isNotEmpty())
                untilBuild.set(pluginUntilBuildProp)

            // Extract the <!-- Plugin description --> section from README.md and provide for the plugin's manifest
            pluginDescription.set(
                projectDir.resolve("../README.md").readText().lines().run {
                    val start = "<!-- Plugin description -->"
                    val end = "<!-- Plugin description end -->"

                    if (!containsAll(listOf(start, end))) {
                        throw GradleException("Plugin description section not found in README.md:\n$start ... $end")
                    }
                    subList(indexOf(start) + 1, indexOf(end))
                }.joinToString("\n").run { markdownToHTML(this) }
            )
            changelog.path.set("../CHANGELOG.md")

            // Get the latest available change notes from the changelog file
            changeNotes.set(provider {
                with(changelog) {
                    renderItem(
                        getOrNull(pluginVersionProp) ?: getLatest(),
                        Changelog.OutputType.HTML,
                    )
                }
            })
        }
    }
}

project(":common") {
}

// Feature plugin for IDEA IDEs
project(":idea") {
    intellij {
        type.set("IC")
        plugins.set(listOf("JUnit", "com.intellij.java"))
    }

    intellij {
        version.set(ideaPlatformVersionProp)
    }

    dependencies {
        implementation(project(":common"))
        implementation(files("libs/ComTest_v1.jar"))
    }
}

// Feature plugin for Rider IDEs
project(":rider") {
    intellij {
        type.set("RD")
    }

    intellij {
        version.set(riderPlatformVersionProp)
    }

    dependencies {
        implementation(project(":common"))
        implementation(files("libs/ComTest_v2.jar"))
    }
}


fun File.isPluginJar(): Boolean {
    if (!isFile) return false
    if (extension != "jar") return false
    return zipTree(this).files.any { it.isManifestFile() }
}

fun File.isManifestFile(): Boolean {
    if (extension != "xml") return false
    val rootNode = try {
        val parser = groovy.xml.XmlParser()
        parser.parse(this)
    } catch (e: Exception) {
        logger.error("Failed to parse $path", e)
        return false
    }
    return rootNode.name() == "idea-plugin"
}

fun prop(key: String) = extra.properties[key] as? String
    ?: error("Property `$key` is not defined in gradle.properties")