/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the "Elastic License
 * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
 * Public License v 1"; you may not use this file except in compliance with, at
 * your election, the "Elastic License 2.0", the "GNU Affero General Public
 * License v3.0 only", or the "Server Side Public License, v 1".
 */

import org.elasticsearch.gradle.internal.test.TestUtil
import org.elasticsearch.gradle.OS
import org.elasticsearch.gradle.VersionProperties

apply plugin: 'elasticsearch.java'
apply plugin: 'elasticsearch.build'


tasks.named("dependencyLicenses").configure {
  mapping from: /lucene-.*/, to: 'lucene'
}

tasks.named('forbiddenApisMain').configure {
  enabled = false
}
repositories {
  mavenLocal()
  maven {
    url = uri("https://storage.googleapis.com/elasticsearch-cuvs-snapshots")
  }
}

dependencies {
  api "org.apache.lucene:lucene-core:${versions.lucene}"
  api "org.apache.lucene:lucene-queries:${versions.lucene}"
  api "org.apache.lucene:lucene-codecs:${versions.lucene}"
  implementation project(':libs:simdvec')
  implementation project(':libs:native')
  implementation project(':libs:logging')
  implementation project(':server')
  implementation project(':x-pack:plugin:gpu')
}
/**
 * Task to run the KnnIndexTester with the provided parameters.
 */
tasks.register("checkVec", JavaExec) {
  group = "Execution"
  description = "Runs KnnIndexTester with the provided parameters to validate recall and performance."
  classpath = sourceSets.main.runtimeClasspath
  mainClass.set("org.elasticsearch.test.knn.KnnIndexTester")
  // Configure logging to console
  systemProperty "es.logger.out", "console"
  systemProperty "es.logger.level", "INFO"  // Change to DEBUG if needed
  systemProperty 'es.nativelibs.path', TestUtil.getTestLibraryPath(file("../../libs/native/libraries/build/platform/").toString())
  jvmArgs '-Xms16g', '-Xmx16g', '-Djava.util.concurrent.ForkJoinPool.common.parallelism=8', '-XX:+UnlockDiagnosticVMOptions', '-XX:+DebugNonSafepoints', '-XX:+HeapDumpOnOutOfMemoryError'
  if (buildParams.getRuntimeJavaVersion().map { it.majorVersion.toInteger() }.get() >= 21) {
    jvmArgs '--add-modules=jdk.incubator.vector', '--enable-native-access=ALL-UNNAMED'
  }
  if (System.getenv("DO_PROFILING") != null) {
    jvmArgs '-XX:StartFlightRecording=dumponexit=true,maxsize=250M,filename=knn.jfr,settings=profile.jfc'
  }
  def asyncProfilerPath = System.getProperty("asyncProfiler.path", null)
  if (asyncProfilerPath != null) {
    def asyncProfilerEvent = System.getProperty("asyncProfiler.event", "cpu")
    if (OS.current().equals(OS.MAC)) {
      def asyncProfilerAgent = "${asyncProfilerPath}/lib/libasyncProfiler.dylib"
      println "Using async-profiler agent ${asyncProfilerAgent}"

      // MacOS implementation of async-profiler does not support wall clock profiling with another event.
      // Wall clock times can be obtained separately invoking this task with `-DasyncProfiler.event=wall`
      jvmArgs "-agentpath:${asyncProfilerAgent}=start,event=${asyncProfilerEvent},interval=10ms,file=${layout.buildDirectory.asFile.get()}/tmp/elasticsearch-0_%t_%p.jfr"
    } else if (OS.current().equals(OS.LINUX)) {
      // Linux implementation of async-profiler uses perf_event, which allows wall clock profiling with another event (cpu)
      def additionalWallInterval = asyncProfilerEvent.equals("cpu") ? ",wall=50ms" : ""

      def asyncProfilerAgent = "${asyncProfilerPath}/lib/libasyncProfiler.so"
      println "Using async-profiler agent ${asyncProfilerAgent}"
      jvmArgs "-agentpath:${asyncProfilerAgent}=start,event=${asyncProfilerEvent},interval=10ms${additionalWallInterval},file=${layout.buildDirectory.asFile.get()}/tmp/elasticsearch-0_%t_%p.jfr"
    } else {
      println "Ignoring 'asyncProfiler.path': not available on ${OS.current()}";
    }
  }
  if (buildParams.getIsRuntimeJavaHomeSet()) {
    executable = "${buildParams.runtimeJavaHome.get()}/bin/java" + (OS.current() == OS.WINDOWS ? '.exe' : '')
  } else {
    javaLauncher = javaToolchains.launcherFor {
      languageVersion = JavaLanguageVersion.of(VersionProperties.bundledJdkMajorVersion)
      vendor = VersionProperties.bundledJdkVendor == "openjdk" ?
        JvmVendorSpec.ORACLE :
        JvmVendorSpec.matching(VersionProperties.bundledJdkVendor)
    }
  }
}

tasks.register("checkVecHelp", JavaExec) {
  group = "Help"
  description = "Prints help for the KnnIndexTester task."
  classpath = sourceSets.main.runtimeClasspath
  mainClass.set("org.elasticsearch.test.knn.KnnIndexTester")
  args = ["--help"]
  doLast {
    println """
    =============================================================================
    KnnIndexTester Help
    =============================================================================

    Run with Gradle:
    ----------------
    # Using default configuration file
    ./gradlew :qa:vector:checkVec

    # Using custom configuration file
    ./gradlew :qa:vector:checkVec --args="path/to/your/config.json"

    # Adjust heap size
    ./gradlew :qa:vector:checkVec -Dorg.gradle.jvmargs="-Xmx8g" --args="path/to/your/config.json"

    # Set environment variable for more extensive JVM options
    export GRADLE_OPTS="-Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=100"
    ./gradlew :qa:vector:checkVec


    Run directly with Java:
    ----------------------
    # Generate classpath (run once to create the file)
    ./gradlew :qa:vector:printClasspath

    # Then use the classpath file with java
    java -cp "\$(cat build/vector_classpath.txt)" \\
         --add-modules=jdk.incubator.vector \\
         --enable-native-access=ALL-UNNAMED \\
         -Djava.util.concurrent.ForkJoinPool.common.parallelism=8 \\
         -Xmx4g \\
         -Xms4g \\\\
         org.elasticsearch.test.knn.KnnIndexTester path/to/your/config.json
    """
  }
}

tasks.register("printClasspath") {
  group = "Help"
  description = "Prints the classpath needed to run KnnIndexTester directly with java"

  doLast {
    def classpathFile = new File("${buildDir}/vector_classpath.txt")
    classpathFile.parentFile.mkdirs()
    classpathFile.text = sourceSets.main.runtimeClasspath.asPath
    println "Classpath written to: ${classpathFile.absolutePath}"
  }
}
