/*
 * 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.apache.tools.ant.filters.ReplaceTokens
import org.elasticsearch.gradle.internal.test.InternalClusterTestPlugin
import org.elasticsearch.gradle.internal.test.RestIntegTestTask

apply plugin: 'elasticsearch.internal-yaml-rest-test'
apply plugin: 'elasticsearch.internal-cluster-test'

esplugin {
  description = 'The Azure Repository plugin adds support for Azure storage repositories.'
  classname ='org.elasticsearch.repositories.azure.AzureRepositoryPlugin'
}

versions << [
  'azureReactorNetty': '1.0.45',
]

dependencies {
  // Microsoft
  api "com.azure:azure-core-http-netty:1.15.3"
  api "com.azure:azure-core:1.51.0"
  api "com.azure:azure-identity:1.13.2"
  api "com.azure:azure-json:1.2.0"
  api "com.azure:azure-storage-blob:12.27.1"
  api "com.azure:azure-storage-blob-batch:12.23.1"
  api "com.azure:azure-storage-common:12.26.1"
  api "com.azure:azure-storage-internal-avro:12.12.1"
  api "com.azure:azure-xml:1.1.0"
  api "com.microsoft.azure:msal4j-persistence-extension:1.3.0"
  api "com.microsoft.azure:msal4j:1.16.2"

  // Jackson
  api "com.fasterxml.jackson.core:jackson-core:${versions.jackson}"
  api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
  api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}"
  api "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${versions.jackson}"
  api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions.jackson}"
  api "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:${versions.jackson}"

  // Netty
  api "io.netty:netty-codec-dns:${versions.netty}"
  api "io.netty:netty-codec-http2:${versions.netty}"
  api "io.netty:netty-codec-socks:${versions.netty}"
  api "io.netty:netty-handler-proxy:${versions.netty}"
  api "io.netty:netty-resolver-dns:${versions.netty}"

  // Reactor
  api "io.projectreactor.netty:reactor-netty-core:${versions.azureReactorNetty}"
  api "io.projectreactor.netty:reactor-netty-http:${versions.azureReactorNetty}"
  api "io.projectreactor:reactor-core:3.4.38"
  api "org.reactivestreams:reactive-streams:${versions.reactive_streams}"

  // Others
  api "com.fasterxml.woodstox:woodstox-core:6.7.0"
  api "com.github.stephenc.jcip:jcip-annotations:1.0-1"
  api "com.nimbusds:content-type:2.3"
  api "com.nimbusds:lang-tag:1.7"
  api("com.nimbusds:nimbus-jose-jwt:10.0.2"){
    exclude group: 'com.google.crypto.tink', module: 'tink' // it's an optional dependency on which we don't rely
  }
  api("com.nimbusds:oauth2-oidc-sdk:11.22.2"){
    exclude group: 'com.google.crypto.tink', module: 'tink' // it's an optional dependency on which we don't rely
  }
  api "jakarta.activation:jakarta.activation-api:1.2.1"
  api "jakarta.xml.bind:jakarta.xml.bind-api:2.3.3"
  api "net.java.dev.jna:jna-platform:${versions.jna}" // Maven says 5.14.0 but this aligns with the Elasticsearch-wide version
  api "net.java.dev.jna:jna:${versions.jna}" // Maven says 5.14.0 but this aligns with the Elasticsearch-wide version
  api "net.minidev:accessors-smart:2.5.2"
  api "net.minidev:json-smart:2.5.2"
  api "org.codehaus.woodstox:stax2-api:4.2.2"
  api "org.ow2.asm:asm:9.8"

  runtimeOnly "com.google.code.gson:gson:2.11.0"
  runtimeOnly "org.cryptomator:siv-mode:1.5.2"

  implementation project(":modules:transport-netty4")
  implementation("org.slf4j:slf4j-api:${versions.slf4j}")
  runtimeOnly "org.slf4j:slf4j-nop:${versions.slf4j}"
//  runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}") https://github.com/elastic/elasticsearch/issues/93714

  testImplementation project(':test:fixtures:azure-fixture')
  yamlRestTestImplementation project(':test:fixtures:azure-fixture')
}

restResources {
  restApi {
    include '_common', 'cluster', 'nodes', 'snapshot', 'bulk', 'count', 'indices'
  }
}

tasks.named("dependencyLicenses").configure {
  mapping from: /azure-.*/, to: 'azure'
  mapping from: /jackson-.*/, to: 'jackson'
  mapping from: /netty-.*/, to: 'netty'
  mapping from: /jaxb-.*/, to: 'jaxb'
  mapping from: /stax-.*/, to: 'stax'
  mapping from: /reactor-netty-.*/, to: 'reactor-netty'
  mapping from: /reactive-streams.*/, to: 'reactive-streams'
}

tasks.named("thirdPartyAudit").configure {
  ignoreMissingClasses(
    // from reactory-netty metric collection
    'io.micrometer.core.instrument.Clock',
    'io.micrometer.core.instrument.Counter',
    'io.micrometer.core.instrument.Counter$Builder',
    'io.micrometer.core.instrument.DistributionSummary',
    'io.micrometer.core.instrument.DistributionSummary$Builder',
    'io.micrometer.core.instrument.Meter',
    'io.micrometer.core.instrument.Meter$Type',
    'io.micrometer.core.instrument.MeterRegistry',
    'io.micrometer.core.instrument.Metrics',
    'io.micrometer.core.instrument.Tag',
    'io.micrometer.core.instrument.Tags',
    'io.micrometer.core.instrument.Timer',
    'io.micrometer.core.instrument.Timer$Builder',
    'io.micrometer.core.instrument.Timer$Sample',
    'io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics',
    'io.micrometer.core.instrument.composite.CompositeMeterRegistry',
    'io.micrometer.core.instrument.search.Search',
    'io.micrometer.core.instrument.Gauge',
    'io.micrometer.core.instrument.Gauge$Builder',
    'io.micrometer.context.ContextAccessor',

    // from reactor-core kotlin extensions (to be deprecated from the library at some point on 3.3.x release)
    'kotlin.collections.ArraysKt',
    'kotlin.jvm.JvmClassMappingKt',
    'kotlin.jvm.functions.Function0',
    'kotlin.jvm.functions.Function1',
    'kotlin.jvm.internal.FunctionReference',
    'kotlin.jvm.internal.Intrinsics',
    'kotlin.jvm.internal.Reflection',
    'kotlin.jvm.internal.markers.KMappedMarker',
    'kotlin.reflect.KClass',
    'kotlin.reflect.KDeclarationContainer',
    'kotlin.sequences.Sequence',

    // from io.netty.util.internal.Hidden (netty-common optional dependency)
    'reactor.blockhound.BlockHound$Builder',
    'reactor.blockhound.integration.BlockHoundIntegration',

    // it uses NIO
    'io.netty.channel.kqueue.KQueue',
    'io.netty.channel.kqueue.KQueueDatagramChannel',
    'io.netty.channel.kqueue.KQueueServerSocketChannel',
    'io.netty.channel.kqueue.KQueueSocketChannel',

    'io.netty.channel.epoll.Epoll',
    'io.netty.channel.epoll.EpollDatagramChannel',
    'io.netty.channel.epoll.EpollServerSocketChannel',
    'io.netty.channel.epoll.EpollSocketChannel',

    'io.netty.incubator.channel.uring.IOUring',
    'io.netty.incubator.channel.uring.IOUringDatagramChannel',
    'io.netty.incubator.channel.uring.IOUringServerSocketChannel',
    'io.netty.incubator.channel.uring.IOUringSocketChannel',

    // from reactor.netty.http.server.HttpServer (reactor-netty)
    'io.netty.handler.codec.haproxy.HAProxyMessage',
    'io.netty.handler.codec.haproxy.HAProxyMessageDecoder',

    // from com.ctc.wstx.osgi.WstxBundleActivator (woodstox-core)
    'org.osgi.framework.BundleActivator',
    'org.osgi.framework.BundleContext',

    // from com.ctc.wstx.shaded.msv_core.driver.textui.Driver (woodstox-core)
    'com.ctc.wstx.shaded.msv_core.driver.textui.Driver',
    // [missing classes] SLF4j includes an optional class that depends on an extension class. see Log4jLogger#createConverter
    //    'org.slf4j.ext.EventData' - bring back when https://github.com/elastic/elasticsearch/issues/93714 is done

    // Optional dependency of tink
    'com.google.crypto.tink.subtle.Ed25519Sign',
    'com.google.crypto.tink.subtle.Ed25519Sign$KeyPair',
    'com.google.crypto.tink.subtle.Ed25519Verify',
    'com.google.crypto.tink.subtle.X25519',
    'com.google.crypto.tink.subtle.XChaCha20Poly1305',

    // Optional dependency of nimbus-jose-jwt and oauth2-oidc-sdk
    'org.bouncycastle.asn1.pkcs.PrivateKeyInfo',
    'org.bouncycastle.asn1.x509.AlgorithmIdentifier',
    'org.bouncycastle.asn1.x509.SubjectPublicKeyInfo',
    'org.bouncycastle.cert.X509CertificateHolder',
    'org.bouncycastle.cert.jcajce.JcaX509CertificateHolder',
    'org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder',
    'org.bouncycastle.openssl.PEMKeyPair',
    'org.bouncycastle.openssl.PEMParser',
    'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter',
    'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder',

    // OAuth servlet support is optional and not required
    'jakarta.servlet.ServletRequest',
    'jakarta.servlet.http.HttpServletRequest',
    'jakarta.servlet.http.HttpServletResponse',
    'javax.servlet.ServletRequest',
    'javax.servlet.http.HttpServletRequest',
    'javax.servlet.http.HttpServletResponse',

    // OpenSAML support is optional
    'org.joda.time.DateTime',
    'net.shibboleth.utilities.java.support.xml.SerializeSupport',
    'org.opensaml.core.config.InitializationException',
    'org.opensaml.core.config.InitializationService',
    'org.opensaml.core.xml.XMLObject',
    'org.opensaml.core.xml.XMLObjectBuilder',
    'org.opensaml.core.xml.XMLObjectBuilderFactory',
    'org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport',
    'org.opensaml.core.xml.io.Marshaller',
    'org.opensaml.core.xml.io.MarshallerFactory',
    'org.opensaml.core.xml.io.MarshallingException',
    'org.opensaml.core.xml.io.Unmarshaller',
    'org.opensaml.core.xml.io.UnmarshallerFactory',
    'org.opensaml.core.xml.schema.XSString',
    'org.opensaml.core.xml.schema.impl.XSStringBuilder',
    'org.opensaml.saml.saml2.core.Assertion',
    'org.opensaml.saml.saml2.core.Attribute',
    'org.opensaml.saml.saml2.core.AttributeStatement',
    'org.opensaml.saml.saml2.core.AttributeValue',
    'org.opensaml.saml.saml2.core.Audience',
    'org.opensaml.saml.saml2.core.AudienceRestriction',
    'org.opensaml.saml.saml2.core.AuthnContext',
    'org.opensaml.saml.saml2.core.AuthnContextClassRef',
    'org.opensaml.saml.saml2.core.AuthnStatement',
    'org.opensaml.saml.saml2.core.Conditions',
    'org.opensaml.saml.saml2.core.Issuer',
    'org.opensaml.saml.saml2.core.NameID',
    'org.opensaml.saml.saml2.core.Subject',
    'org.opensaml.saml.saml2.core.SubjectConfirmation',
    'org.opensaml.saml.saml2.core.SubjectConfirmationData',
    'org.opensaml.saml.security.impl.SAMLSignatureProfileValidator',
    'org.opensaml.security.credential.BasicCredential',
    'org.opensaml.security.credential.Credential',
    'org.opensaml.security.credential.UsageType',
    'org.opensaml.xmlsec.signature.Signature',
    'org.opensaml.xmlsec.signature.support.SignatureException',
    'org.opensaml.xmlsec.signature.support.SignatureValidator',
    'org.opensaml.xmlsec.signature.support.Signer',
  )

  ignoreViolations(
    'javax.activation.MailcapCommandMap',
    'javax.activation.MimetypesFileTypeMap',
    'reactor.core.publisher.Traces$SharedSecretsCallSiteSupplierFactory$TracingException',
  )
}

boolean useFixture = false
String azureAccount = System.getenv("azure_storage_account")
String azureKey = System.getenv("azure_storage_key")
String azureContainer = System.getenv("azure_storage_container")
String azureBasePath = System.getenv("azure_storage_base_path")
String azureSasToken = System.getenv("azure_storage_sas_token")
String azureTenantId = System.getenv("azure_storage_tenant_id")
String azureClientId = System.getenv("azure_storage_client_id")

if (!azureAccount && !azureKey && !azureContainer && !azureBasePath && !azureSasToken) {
  azureAccount = 'azure_integration_test_account'
  azureKey = 'YXp1cmVfaW50ZWdyYXRpb25fdGVzdF9rZXk=' // The key is "azure_integration_test_key" encoded using base64
  azureContainer = 'container'
  azureBasePath = ''
  azureSasToken = ''
  azureTenantId = ''
  azureClientId = ''
  useFixture = true
}

Map<String, String> expansions = [
  'container': azureContainer,
  'base_path': azureBasePath + "_integration_tests"
]

tasks.named("processYamlRestTestResources") {
  inputs.properties(expansions)
  filter("tokens" : expansions, ReplaceTokens.class)
}

tasks.named("internalClusterTest") {
  // this is tested explicitly in a separate test task
  exclude '**/AzureStorageCleanupThirdPartyTests.class'
  systemProperty "AZURE_POD_IDENTITY_AUTHORITY_HOST", "127.0.0.1:1" // ensure a fast failure
}

tasks.named("yamlRestTest") {
  systemProperty 'test.azure.fixture', Boolean.toString(useFixture)
  systemProperty 'test.azure.account', azureAccount
  systemProperty 'test.azure.container', azureContainer
  systemProperty 'test.azure.key', azureKey
  systemProperty 'test.azure.sas_token', azureSasToken
}

tasks.register("managedIdentityYamlRestTest", RestIntegTestTask) {
  testClassesDirs = sourceSets.yamlRestTest.output.classesDirs
  classpath = sourceSets.yamlRestTest.runtimeClasspath
  systemProperty 'test.azure.fixture', Boolean.toString(useFixture)
  systemProperty 'test.azure.account', azureAccount
  systemProperty 'test.azure.container', azureContainer
  // omitting key and sas_token so that we use a bearer token from the metadata service
}

tasks.register("workloadIdentityYamlRestTest", RestIntegTestTask) {
  testClassesDirs = sourceSets.yamlRestTest.output.classesDirs
  classpath = sourceSets.yamlRestTest.runtimeClasspath
  systemProperty 'test.azure.fixture', Boolean.toString(useFixture)
  systemProperty 'test.azure.account', azureAccount
  systemProperty 'test.azure.container', azureContainer
  // random uuids to satisfy format requirements -- the actual values are not important
  systemProperty 'test.azure.tenant_id', azureTenantId ?: "583d4f71-148a-4163-bad5-2311e13c60dc"
  systemProperty 'test.azure.client_id', azureClientId ?: "86dd1b33-96c1-4a2e-92ac-b844404fc691"
  // omitting key and sas_token so that we use a bearer token from workload identity
}

if (buildParams.inFipsJvm) {
  // Cannot override the trust store in FIPS mode, and these tasks require a HTTPS fixture
  tasks.named("managedIdentityYamlRestTest").configure { enabled = false }
  tasks.named("workloadIdentityYamlRestTest").configure { enabled = false }
}

tasks.register("azureThirdPartyUnitTest", Test) {
  SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
  SourceSet internalTestSourceSet = sourceSets.getByName(InternalClusterTestPlugin.SOURCE_SET_NAME)
  setTestClassesDirs(internalTestSourceSet.getOutput().getClassesDirs())
  setClasspath(internalTestSourceSet.getRuntimeClasspath())
  include '**/AzureStorageCleanupThirdPartyTests.class'
  systemProperty 'test.azure.fixture', Boolean.toString(useFixture)
  systemProperty 'test.azure.account', azureAccount ?: ""
  systemProperty 'test.azure.key', azureKey ?: ""
  systemProperty 'test.azure.sas_token', azureSasToken ?: ""
  systemProperty 'test.azure.container', azureContainer ?: ""
  systemProperty 'test.azure.base', (azureBasePath ?: "") + "_third_party_tests_"
}

tasks.register('azureThirdPartyTest') {
  dependsOn 'azureThirdPartyUnitTest', 'yamlRestTest'
}

tasks.named("check") {
  dependsOn("azureThirdPartyUnitTest")
  dependsOn("managedIdentityYamlRestTest")
  dependsOn("workloadIdentityYamlRestTest")
}
