/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 *
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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.
 */

import org.gradle.internal.jvm.Jvm
import org.gradle.util.GradleVersion

plugins {
  id 'java-gradle-plugin'
  id 'groovy'
  id 'java-test-fixtures'
}

group = 'org.opensearch.gradle'

String minimumGradleVersion = file('src/main/resources/minimumGradleVersion').text.trim()
if (GradleVersion.current() < GradleVersion.version(minimumGradleVersion)) {
  throw new GradleException("Gradle ${minimumGradleVersion}+ is required to build opensearch")
}

if (project == rootProject) {
  // change the build dir used during build init, so that doing a clean
  // won't wipe out the buildscript jar
  buildDir = 'build-bootstrap'
}

/*****************************************************************************
 *         Propagating version.properties to the rest of the build           *
 *****************************************************************************/

// we update the version property to reflect if we are building a snapshot or a release build
// we write this back out below to load it in the Build.java which will be shown in rest main action
// to indicate this being a snapshot build or a release build.
Properties props = VersionPropertiesLoader.loadBuildSrcVersion(project.file('version.properties'))
version = props.getProperty("opensearch")

def generateVersionProperties = tasks.register("generateVersionProperties", WriteProperties) {
  outputFile = "${buildDir}/version.properties"
  comment = 'Generated version properties'
  properties(props)
}

processResources {
  from(generateVersionProperties)
}

/*****************************************************************************
 *         Java version                                                      *
 *****************************************************************************/

if (JavaVersion.current() < JavaVersion.VERSION_11) {
  throw new GradleException('At least Java 11 is required to build opensearch gradle tools')
}

sourceSets {
  integTest {
    compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
    runtimeClasspath += output + compileClasspath
  }
}

tasks.withType(JavaCompile).configureEach {
  options.encoding = 'UTF-8'
}

/*****************************************************************************
 *                    Dependencies used by the entire build                  *
 *****************************************************************************/

repositories {
  jcenter()
}

dependencies {

  api localGroovy()

  api 'commons-codec:commons-codec:1.12'
  api 'org.apache.commons:commons-compress:1.19'
  api 'org.apache.ant:ant:1.10.8'
  api 'com.netflix.nebula:gradle-extra-configurations-plugin:3.0.3'
  api 'com.netflix.nebula:nebula-publishing-plugin:4.4.4'
  api 'com.netflix.nebula:gradle-info-plugin:7.1.3'
  api 'org.apache.rat:apache-rat:0.11'
  api "org.elasticsearch:jna:5.5.0"
  api 'com.github.jengelman.gradle.plugins:shadow:6.0.0'
  api 'de.thetaphi:forbiddenapis:3.0'
  api 'com.avast.gradle:gradle-docker-compose-plugin:0.12.1'
  api 'org.apache.maven:maven-model:3.6.2'
  api 'com.networknt:json-schema-validator:1.0.36'

  compileOnly "com.puppycrawl.tools:checkstyle:${props.getProperty('checkstyle')}"
  testImplementation "com.puppycrawl.tools:checkstyle:${props.getProperty('checkstyle')}"
  testFixturesApi "junit:junit:${props.getProperty('junit')}"
  testFixturesApi "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${props.getProperty('randomizedrunner')}"
  testFixturesApi gradleApi()
  testFixturesApi gradleTestKit()
  testImplementation 'com.github.tomakehurst:wiremock-jre8-standalone:2.23.2'
  testImplementation 'org.mockito:mockito-core:1.9.5'
  integTestImplementation('org.spockframework:spock-core:1.3-groovy-2.5') {
    exclude module: "groovy"
  }
}

/*****************************************************************************
 *                         Bootstrap repositories                            *
 *****************************************************************************/
// this will only happen when buildSrc is built on its own during build init
if (project == rootProject) {
  repositories {
    if (System.getProperty("repos.mavenLocal") != null) {
      mavenLocal()
    }
  }
  dependencies {
    // add this so the runtime classpath so Gradle will properly track it as a build runtime classpath input
    runtimeOnly project('reaper')
  }
  // only run tests as build-tools
  test.enabled = false
}

/*****************************************************************************
 *                           Normal project checks                           *
 *****************************************************************************/

// this happens when included as a normal project in the build, which we do
// to enforce precommit checks like forbidden apis, as well as setup publishing
if (project != rootProject) {
  apply plugin: 'opensearch.build'
  apply plugin: 'opensearch.publish'

  allprojects {
    targetCompatibility = 10
    sourceCompatibility = 10
  }

  // groovydoc succeeds, but has some weird internal exception...
  groovydoc.enabled = false

  // build-tools is not ready for primetime with these...
  tasks.named("dependencyLicenses").configure { it.enabled = false }
  dependenciesInfo.enabled = false
  disableTasks('forbiddenApisMain', 'forbiddenApisTest', 'forbiddenApisIntegTest', 'forbiddenApisTestFixtures')
  jarHell.enabled = false
  thirdPartyAudit.enabled = false
  if (org.opensearch.gradle.info.BuildParams.inFipsJvm) {
    // We don't support running gradle with a JVM that is in FIPS 140 mode, so we don't test it.
    // WaitForHttpResourceTests tests would fail as they use JKS/PKCS12 keystores
    test.enabled = false
    testingConventions.enabled = false
  }

  configurations.register("distribution")
  configurations.register("reaper")

  dependencies {
    reaper project('reaper')
    distribution project(':distribution:archives:oss-windows-zip')
    distribution project(':distribution:archives:oss-darwin-tar')
    distribution project(':distribution:archives:oss-linux-tar')
    distribution project(':distribution:archives:oss-linux-aarch64-tar')

    integTestRuntimeOnly(project(":libs:opensearch-core"))
  }

  // for external projects we want to remove the marker file indicating we are running the OpenSearch project
  processResources {
    exclude 'buildSrc.marker'
    into('META-INF') {
      from configurations.reaper
    }
  }

  // Track reaper jar as a test input using runtime classpath normalization strategy
  tasks.withType(Test).configureEach {
    inputs.files(configurations.reaper).withNormalizer(ClasspathNormalizer)
  }

  normalization {
    runtimeClasspath {
      // We already include the reaper jar as part of our runtime classpath. Ignore the copy in META-INF.
      ignore('META-INF/reaper.jar')
    }
  }

  forbiddenPatterns {
    exclude '**/*.wav'
    exclude '**/*.p12'
    exclude '**/*.jks'
    exclude '**/*.crt'
    // the file that actually defines nocommit
    exclude '**/ForbiddenPatternsTask.java'
    exclude '**/*.bcfks'
  }

  testingConventions {
    naming.clear()
    naming {
      Tests {
        baseClass 'org.opensearch.gradle.test.GradleUnitTestCase'
      }
      IT {
        baseClass 'org.opensearch.gradle.test.GradleIntegrationTestCase'
      }
    }
  }

  tasks.register("integTest", Test) {
    inputs.dir(file("src/testKit")).withPropertyName("testkit dir").withPathSensitivity(PathSensitivity.RELATIVE)
    systemProperty 'test.version_under_test', version
    onlyIf { org.opensearch.gradle.info.BuildParams.inFipsJvm == false }
    maxParallelForks = System.getProperty('tests.jvms', org.opensearch.gradle.info.BuildParams.defaultParallel.toString()) as Integer
    testClassesDirs = sourceSets.integTest.output.classesDirs
    classpath = sourceSets.integTest.runtimeClasspath
  }
  check.dependsOn("integTest")

  // for now we hardcode the tests for our build to use the gradle jvm.
  tasks.withType(Test).configureEach {
    it.executable = Jvm.current().getJavaExecutable()
  }

  /*
   * We already configure publication and we don't need or want this one that
   * comes from the java-gradle-plugin.
   */
  afterEvaluate {
    generatePomFileForPluginMavenPublication.enabled = false
  }

  publishing.publications.named("nebula").configure {
    suppressPomMetadataWarningsFor("testFixturesApiElements")
    suppressPomMetadataWarningsFor("testFixturesRuntimeElements")
  }
}

// Define this here because we need it early.
class VersionPropertiesLoader {
  static Properties loadBuildSrcVersion(File input) throws IOException {
    Properties props = new Properties();
    InputStream is = new FileInputStream(input)
    try {
      props.load(is)
    } finally {
      is.close()
    }
    loadBuildSrcVersion(props, System.getProperties())
    return props
  }

  protected static void loadBuildSrcVersion(Properties loadedProps, Properties systemProperties) {
    String opensearch = loadedProps.getProperty("opensearch")
    if (opensearch == null) {
      throw new IllegalStateException("OpenSearch version is missing from properties.")
    }
    if (opensearch.matches("[0-9]+\\.[0-9]+\\.[0-9]+") == false) {
      throw new IllegalStateException(
        "Expected opensearch version to be numbers only of the form  X.Y.Z but it was: " +
          opensearch
      )
    }
    String qualifier = systemProperties.getProperty("build.version_qualifier", "");
    if (qualifier.isEmpty() == false) {
      if (qualifier.matches("(alpha|beta|rc)\\d+") == false) {
        throw new IllegalStateException("Invalid qualifier: " + qualifier)
      }
      opensearch += "-" + qualifier
    }
    final String buildSnapshotSystemProperty = systemProperties.getProperty("build.snapshot", "true");
    switch (buildSnapshotSystemProperty) {
      case "true":
        opensearch += "-SNAPSHOT"
        break;
      case "false":
        // do nothing
        break;
      default:
        throw new IllegalArgumentException(
          "build.snapshot was set to [" + buildSnapshotSystemProperty + "] but can only be unset or [true|false]");
    }
    loadedProps.put("opensearch", opensearch)
  }
}