plugins {
    id 'java'
    id 'groovy'	
    id 'eclipse'
    id "de.undercouch.download" version "5.0.1"
    id 'war' // Handles the WAR layout mechanics natively
}
import java.nio.file.Files
import java.nio.file.Path
// Project Metadata
group = 'org.thoughtframe'
def buildVersion = findProperty('version') ?: 'local'
version = buildVersion
description = 'thoughtframe project'

// Compile with JDK 25 regardless of the JDK running Gradle (foojay auto-provisions it).
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(25)
    }
}

// Path Settings
def srcDir = 'src'
def testSrcDir = 'test'
def resourcesDir = 'resources'
def libDir = 'lib'
def deployDir = 'deploy'
def emDir = 'dependancies/entermedia' //

def framebranch = findProperty('framebranch') ?: 'dev_'

repositories {
    mavenLocal()    // 🌟 Locates your new entermedia snapshots
    mavenCentral()  // Pulls down modern open-source equivalents
	gradlePluginPortal()
	maven { url 'https://repo.gradle.org/gradle/libs-releases' }
	
}

dependencies {
    // 1. Core Framework Foundation
    api project(':runtime-api')
    implementation project(':dam')
	
	
	// Unified SLF4J 2.x Logging Ecosystem
    implementation 'org.slf4j:slf4j-api:2.0.12'
    implementation 'org.slf4j:slf4j-jdk14:2.0.12'

    // Runtime Request Interception Bridges
    implementation 'org.slf4j:jcl-over-slf4j:2.0.12'    
    implementation 'org.slf4j:log4j-over-slf4j:2.0.12'
	
	// Embedded Jetty Engine for local launching/testing
	// 🔄 Update the group ID for the ee8-webapp module:
    implementation 'org.eclipse.jetty:jetty-server:12.0.35'
    implementation 'org.eclipse.jetty.ee8:jetty-ee8-webapp:12.0.35' // 🌟 FIXES: Unresolved dependency error!	
	implementation 'org.eclipse.jetty.ee8.websocket:jetty-ee8-websocket-javax-server:12.0.35'	
	implementation 'org.eclipse.jetty:jetty-util:12.0.35'
	// 2. Specialized Document Engines
    implementation 'com.itextpdf:itextpdf:5.5.7'
    implementation 'com.itextpdf:itext-pdfa:5.5.7'
    implementation 'com.itextpdf.tool:xmlworker:5.5.7' // 🌟 FIXES: xmlworker group ID coordinates
    
    // 🌟 FIXES: commons-imaging SNAPSHOT error by cleanly disabling it inside itext-xtra
    implementation('com.itextpdf:itext-xtra:5.5.7') {
        exclude group: 'org.apache.commons', module: 'commons-imaging'
    }
    
    // ODF & Document Translators
    implementation 'org.odftoolkit:odfdom-java:0.8.7'
    implementation 'fr.opensagres.xdocreport:org.odftoolkit.odfdom.converter.core:1.0.0'
    implementation 'fr.opensagres.xdocreport:org.odftoolkit.odfdom.converter.pdf:1.0.0'
    implementation 'fr.opensagres.xdocreport:org.odftoolkit.odfdom.converter.xhtml:1.0.0'

    // 3. Git Automation Suite
    implementation 'org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r'
    implementation 'org.eclipse.jgit:org.eclipse.jgit.http.server:6.8.0.202311291450-r'

    // 4. Modern I/O Network Clients & Utilities
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.squareup.okhttp3:okhttp-sse:4.12.0'
    implementation 'com.squareup.okio:okio:3.6.0'
    implementation 'com.neovisionaries:nv-websocket-client:2.12'
    implementation 'com.jayway.jsonpath:json-path:2.7.0'

    // 5. Payment Gateways, Subscriptions, & Feeds
    implementation 'com.stripe:stripe-java:22.28.0'
    implementation 'com.paypal.sdk:rest-api-sdk:1.14.0'
    implementation 'org.python:jython-standalone:2.7.2'
    implementation 'org.apache.commons:commons-math4-core:4.0-beta1'

    // 6. Base Scripting, Mail & Platform Protocols
    compileOnly "org.apache.groovy:groovy-all:6.0.0" //
    testImplementation "org.apache.groovy:groovy-all:6.0.0" //
    implementation "org.eclipse.angus:angus-mail:2.0.3" //
    implementation "jakarta.mail:jakarta.mail-api:2.1.3" //
    implementation "jakarta.activation:jakarta.activation-api:2.1.3" //
    implementation 'org.gradle:gradle-tooling-api:8.10'
	implementation 'javax.websocket:javax.websocket-api:1.1'
	implementation 'commons-lang:commons-lang:2.6'
    // 7. Local Custom Jars ONLY (Including pngj to bypass coordinate headaches!)
    implementation fileTree(dir: 'lib', include: [
        'jcsAnetGWv1_02.jar',
        'paypal_base.jar',
        'openecho1.2.6.jar',
        'store-reports.jar',
        'tincan-1.1.0.jar',


    ])

    // 8. Testing
    // `test` is JUnit 5 (Jupiter). The old suite is JUnit 3 (junit.framework.TestCase via
    // BaseTestCase/StoreTestCase) and lives in src/legacyTest - see the legacyTest task below.
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.12.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.12.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.12.0'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.0'
}

// OpenSearch k-NN ships only as a plugin distribution zip (no consumable jar), so unpack it
// and put its jars on the runtime classpath. The embedded node loads the plugin by classname
// (node.xml opensearch.plugins) and the engine stages this runtime classpath, so shipping it
// here is what puts it in eclipse/engine. Same shape as the product :damsel build.
configurations {
    knnPlugin
}

def knnLibsDir = layout.buildDirectory.dir('knn-plugin').get().asFile

tasks.register('unpackKnnPlugin', Copy) {
    description = 'Unpack the OpenSearch k-NN plugin zip onto the runtime classpath'
    from({ zipTree(configurations.knnPlugin.singleFile) })
    into knnLibsDir
}

tasks.named('compileJava') {
    dependsOn 'unpackKnnPlugin'
}

dependencies {
    knnPlugin 'org.opensearch.plugin:opensearch-knn:3.8.0.0@zip'
    // The plugin expects these from the OpenSearch distribution's lib dir / SPI, which the
    // maven artifacts do not provide transitively (the same set :damsel declares). The
    // bundled slf4j is dropped - thoughtframe already provides slf4j-api.
    runtimeOnly fileTree(dir: knnLibsDir, include: '*.jar', exclude: 'slf4j-api-*.jar')
    runtimeOnly 'com.google.guava:guava:33.4.8-jre'
    runtimeOnly 'org.opensearch.plugin:transport-grpc:3.8.0'
    runtimeOnly 'org.opensearch.plugin:lang-painless:3.8.0'
}

// ---------------------------------------------------------------------------
// Legacy JUnit 3 suite (src/legacyTest). Kept runnable so it can be rebuilt
// incrementally, but NOT wired into `check`/`build` - it has pre-existing
// failures against the current catalog fixture. Run with:
//     ./gradlew :thoughtframe:legacyTest
// ---------------------------------------------------------------------------
sourceSets {
    legacyTest {
        java.srcDir 'src/legacyTest/java'
        compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath
        runtimeClasspath += output + compileClasspath
    }
}

configurations {
    legacyTestImplementation.extendsFrom testImplementation
    legacyTestRuntimeOnly.extendsFrom testRuntimeOnly
}

dependencies {
    legacyTestImplementation 'junit:junit:4.13.2'
    legacyTestRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.12.0'
}

tasks.register('legacyTest', Test) {
    group = 'verification'
    description = 'Runs the legacy JUnit 3 suite in src/legacyTest (opt-in; has known failures).'
    testClassesDirs = sourceSets.legacyTest.output.classesDirs
    classpath = sourceSets.legacyTest.runtimeClasspath
    useJUnitPlatform()
    testLogging { events "failed", "skipped" }
    // Only the AllTests aggregators, otherwise every case runs twice (once via its
    // suite, once via direct class discovery).
    filter {
        includeTestsMatching '*AllTests'
    }
    shouldRunAfter tasks.named('test')
}


tasks.register("setupLinks") {
    group = "thoughtframe"
    description = "Creates the local development runtime."

    doLast {

        def runtime = projectDir.toPath().resolve("webapp")
        def base = runtime.resolve("WEB-INF/base")

        Files.createDirectories(base)

        // Stale generated symlinks from older setupLinks runs must not linger: they carry
        // machine-specific absolute targets and break createWar on any other host (e.g. CI).
        ["emfrontend", "emshare"].each { n ->
            def stale = base.resolve(n)
            if (Files.isSymbolicLink(stale) || Files.exists(stale)) {
                println "Removing stale ${stale}"
                Files.delete(stale)
            }
        }

        def links = [
            [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/entermedia"), link: base.resolve("entermedia")],
            [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/emfrontend2"), link: base.resolve("emfrontend2")],
            [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/system"),      link: base.resolve("system")],
            [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/manager"),     link: base.resolve("manager")],
            [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/mediadb"),     link: base.resolve("mediadb")],
            [target: project(":cms").projectDir.toPath().resolve("webapp/openedit"),                 link: base.resolve("openedit")]
        ]

        links.each {

            if (Files.exists(it.link) || Files.isSymbolicLink(it.link)) {
                Files.delete(it.link)
            }

            println "Linking ${it.link} -> ${it.target}"
            Files.createSymbolicLink(it.link, it.target)
        }
    }
}

tasks.named("war") {
	dependsOn("setupLinks")
}

// Compilation Hooks linked cleanly without inline class injection loops
tasks.named('compileGroovy') {
    groovyOptions.encoding = 'UTF-8' //
    options.release = 25 //
}

tasks.named('compileJava') {
    options.encoding = 'UTF-8' //
    options.release = 25 //
}

tasks.named('test') {
    useJUnitPlatform() //
    testClassesDirs = sourceSets.test.output.classesDirs //
    testLogging { events "passed", "skipped", "failed" } //
}
tasks.named('clean') { delete deployDir } //

jar {
    dependsOn 'classes' //
    archiveBaseName.set("${framebranch}thoughtframe") //
    archiveVersion.set(version) //
    destinationDirectory.set(file("$deployDir/builds")) //
}

// 📦 Streamlined production Web Deployment construction
task createWar(type: War) {
    dependsOn jar, setupLinks // setupLinks for the local run; the WAR copies the bases directly
    archiveBaseName.set("ROOT") //
    archiveFileName.set("ROOT.war") // fixed name — the runtime bakes docker/runtime/ROOT.war
    destinationDirectory.set(file("$deployDir")) //
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE //
    // Product code lives in WEB-INF/lib only. The War task otherwise routes the classpath
    // dirs into WEB-INF/classes, which shadow the jar and are never refreshed by the
    // runtime's overlay upgrade (entrypoint deletes them on first boot as a safety net).
    classpath = files()

 
    
    // 2. Automate Library Assemblies via Runtime Classpath (No manual listing needed!)
    from(configurations.runtimeClasspath) { into "WEB-INF/lib" } //
    
    // 3. Stage ThoughtFrame core components
    from("webapp") { //
        include "WEB-INF/base/thoughtframe/**" //
        include "WEB-INF/base/_site.xconf" //
        include "WEB-INF/bin/**" //
        include "WEB-INF/data/system/**" //
        include "WEB-INF/*.*" //
        include "*.*" //
    }

    from("webapp/WEB-INF/base/thoughtframe") { into "WEB-INF/base/thoughtframe" } //

    // Platform bases copied DIRECTLY from the sibling projects — not via the setupLinks
    // symlinks (host-specific, and dangling on a fresh checkout / CI).
    from(project(":dam").file("webapp/WEB-INF/base/entermedia")) { into "WEB-INF/base/entermedia" }
    from(project(":dam").file("webapp/WEB-INF/base/emfrontend2")) { into "WEB-INF/base/emfrontend2" }
    from(project(":dam").file("webapp/WEB-INF/base/system"))     { into "WEB-INF/base/system" }
    from(project(":dam").file("webapp/WEB-INF/base/manager"))    { into "WEB-INF/base/manager" }
    from(project(":dam").file("webapp/WEB-INF/base/mediadb"))    { into "WEB-INF/base/mediadb" }
    from(project(":cms").file("webapp/openedit"))                { into "WEB-INF/base/openedit" } //

    // 4. Fallback for files that are strictly local or un-mavenized
    from(libDir) { //
        include "jcsAnet*.jar" //
        include "openecho*.jar" //
        include "paypal*.jar" //
        into "WEB-INF/lib" //
    }

    // 5. Inject your compiled core application bundle
    from("$deployDir/builds") { //
        include "${framebranch}thoughtframe-${version}.jar" //
        into "WEB-INF/lib" //
    }

    manifest {
        attributes(
            "Built-By": "THOUGHTFRAME.AI", //
            "Implementation-Title": project.name, //
            "Implementation-Version": version, //
            "Implementation-Vendor": "ThoughtFrame.AI" //
        )
    }
}





task buildAll {
    group = 'build' //
    description = 'Run the full build process' //
    dependsOn  clean, jar, createWar //
}

defaultTasks 'buildAll' //
// Updated configurations to force Eclipse execution priority

// The Eclipse bin/ output tree is not a build input, but a stale resource copy inside it can
// collide with the real one under src/ - Gradle 9 fails on duplicate archive entries where older
// versions silently included them. Same strategy thoughtframe's createWar already sets.
tasks.withType(Jar).configureEach {
	duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
