/*
 * Copyright Thoughtworks, Inc.
 *
 * Licensed 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 com.github.jk1.license.task.ReportTask
import com.thoughtworks.go.build.LicenseReport
import com.thoughtworks.go.build.TextUtils
import com.thoughtworks.go.build.VerifyJarTask
import groovy.json.JsonSlurper

import static com.thoughtworks.go.build.JRuby.bundledGemRubyVersion

description = 'GoCD Server'

sourceSets {
  integrationTest {
    java {
      srcDirs = [project.file('src/test-integration/java')]
    }
    resources {
      srcDirs = [project.file('src/test-integration/resources')]
    }
  }
}

configurations {
  integrationTestImplementation.extendsFrom testImplementation

  warConfig

  copyOnly {
    transitive = false
  }

  copyOnlyTestData {
    transitive = false
  }

  apiBase
  allApis
  spark

  railsRuntimeClasspath {
    extendsFrom runtimeClasspath
    extendsFrom spark
    extendsFrom apiBase
  }

  railsTestRuntimeClasspath {
    extendsFrom railsRuntimeClasspath
    extendsFrom testRuntimeClasspath
  }
}

def webAppDir = file("src/main/webapp")
project.ext.railsRoot = file("${webAppDir}/WEB-INF/rails")

dependencies {
  compileOnly project.deps.jetBrainsAnnotations

  apiBase project(':api:api-base')
  allApis project(':api').subprojects
  spark project(':spark').subprojects
  implementation project(':config:config-api')
  api project(':config:config-server')
  api(project(':plugin-infra:go-plugin-access')) { capabilities { requireFeature('server-only') } }
  api project(':plugin-infra:go-plugin-domain')
  api project(':plugin-infra:go-plugin-infra')
  api project(':plugin-infra:go-plugin-config-repo')
  implementation project(':plugin-infra:plugin-metadata-store')
  api project(':common')
  implementation project(':commandline')
  implementation project(':util')
  runtimeOnly project(':rack_hack')
  implementation project(':db-support:db-migration')
  implementation project(':db-support:db-support-base')
  runtimeOnly project(':db-support:db-support-h2')
  runtimeOnly project(':db-support:db-support-postgresql')
  runtimeOnly project(':db-support:db-support-mysql')

  packagingOnly project.deps.servletApi

  copyOnly project(path: ':tfs-impl:tfs-impl-14', configuration: 'fatJarConfig')
  copyOnly project(path: ':agent', configuration: 'fatJarConfig')
  copyOnly project(path: ':agent-launcher', configuration: 'fatJarConfig')
  copyOnlyTestData(testFixtures(project(':test:test-agent')))

  implementation project.deps.commonsCodec
  implementation project.deps.commonsCollections4
  implementation project.deps.commonsDbcp
  implementation project.deps.commonsIO
  implementation project.deps.commonsLang3
  implementation project.deps.commonsText

  implementation project.deps.caffeine
  implementation project.deps.quartz

  api(project.deps.hibernate) {
    exclude(module: 'ehcache-core')
  }

  if (project.deps.hibernate) {
    // Required by Hibernate 3.6 but Hibernate declares as an 'optional' dependency.
    // Remove for later Hibernate versions where it is declared as compulsory, so we can use a validated version
    runtimeOnly project.deps.javassist
  }
  implementation project.deps.mybatis
  implementation project.deps.mybatisSpring
  implementation project.deps.ehcache
  implementation(project.deps.oscache) {
    exclude(module: 'jms')
  }

  implementation project.deps.activeMQ

  implementation project.deps.gson
  implementation project.deps.jdom
  implementation project.deps.dom4j

  implementation project.deps.jruby
  runtimeOnly project.deps.rack
  implementation project.deps.apacheHttpComponents

  implementation project.deps.jakartaAnnotation
  implementation project.deps.springOrm
  implementation project.deps.springWeb
  implementation project.deps.springWebmvc
  implementation project.deps.springContextSupport
  api project.deps.springSecurity
  implementation project.deps.springSecurityWeb
  implementation project.deps.springTx
  if (project.deps.springWeb) {
    runtimeOnly project.deps.commonsFileUpload // used by CommonsMultipartResolver
  }
  if (project.deps.commonsDbcp || project.deps.oscache || project.deps.springWeb) {
    runtimeOnly project.deps.slf4jJcl // Allow commons-logging replacement
  }
  implementation project.deps.aspectj
  runtimeOnly project.deps.urlrewrite

  runtimeOnly project.deps.freemarker
  implementation project.deps.jakartaMail
  runtimeOnly project.deps.angusMailSmtp

  implementation project.deps.slf4jApi
  implementation(project.deps.jgitServer) {
    exclude(module: 'jsch')
    exclude(module: 'jzlib')
  }
  compileOnly project(':app-server')
  annotationProcessor project.deps.lombok
  compileOnly project.deps.lombok
  implementation project.deps.logback
  compileOnly project.deps.jettyWebsocket

  testImplementation(testFixtures(project(':config:config-api')))
  testImplementation(testFixtures(project(':config:config-server')))
  testImplementation(testFixtures(project(':common')))

  testImplementation project(':app-server')
  testRuntimeOnly project(':jetty')
  testImplementation project.deps.jetty
  testImplementation project.deps.jettyWebapp
  testImplementation project.deps.jettyWebsocket
  testImplementation project.deps.freemarker
  testImplementation project.deps.jsoup
  testImplementation project.deps.assertJ
  testImplementation project.deps.awaitility
  testImplementation project.deps.xmlUnit

  testImplementation(testFixtures(project(':test:http-mocks')))

  testImplementation project.deps.junit5Api
  testImplementation project.deps.junit5Params
  testRuntimeOnly project.deps.junit5Engine
  testRuntimeOnly project.deps.junit5PlatformLauncher
  testImplementation project.deps.mockitoCore
  testImplementation project.deps.mockitoJunit5
  testImplementation project.deps.systemStubs
  testImplementation project.deps.jsonUnit
  testImplementation project.deps.springTest
  testImplementation(testFixtures(project(':domain')))

  integrationTestImplementation(project(':base')) { capabilities { requireFeature('agent-client') } }
  integrationTestImplementation project.deps.urlrewrite
  integrationTestImplementation project.deps.springTest
  integrationTestImplementation project.deps.springTestJunit5
  integrationTestImplementation(testFixtures(project(":domain")))
  integrationTestImplementation(project.deps.dbunit) {
    exclude(group: 'org.postgresql')
    exclude(group: 'com.oracle.database.jdbc')
  }

  testFixturesApi project(':domain')
  testFixturesImplementation project(':util')
  testFixturesImplementation project(':config:config-api')
  testFixturesImplementation project(':config:config-server')
  testFixturesImplementation project(':plugin-infra:go-plugin-access')

  testFixturesImplementation(testFixtures(project(':config:config-api')))
  testFixturesImplementation(testFixtures(project(':config:config-server')))
  testFixturesImplementation(testFixtures(project(':domain')))
  testFixturesImplementation project.deps.mockitoCore
  testFixturesImplementation project.deps.assertJ

  testFixturesImplementation project.deps.commonsCollections4
  testFixturesImplementation project.deps.commonsLang3
  testFixturesImplementation project.deps.commonsIO
  testFixturesImplementation project.deps.springContext
  testFixturesApi project.deps.springSecurity
  testFixturesApi project.deps.springOrm
  testFixturesImplementation project.deps.springTx
  testFixturesImplementation project.deps.mybatis
  testFixturesApi project.deps.servletApi
}

jar {
  archiveClassifier = 'classes'
}

idea {
  module {
    testSources.from(sourceSets.integrationTest.java.srcDirs)
  }
}

tasks.register('cleanDb', Delete) {
  delete "db/h2db"
  delete "db/config.git"
}

clean.dependsOn cleanDb

tasks.register('cleanTestRepo', Delete) {
  delete 'test-repo'
}

clean.dependsOn cleanTestRepo

tasks.register('prepareTestGitRepo') {
  File repoDir = project.file('test-repo')
  outputs.dir repoDir

  def injected = project.objects.newInstance(Injected)
  doFirst {
    delete(repoDir)
    repoDir.mkdirs()

    injected.execOps.with {
      exec {
        workingDir = repoDir
        commandLine = ['git', 'init', '--initial-branch=master']
        standardOutput = new ByteArrayOutputStream()
      }
      exec {
        workingDir = repoDir
        commandLine = ['git', 'config', 'commit.gpgsign', 'false']
      }
      file("${repoDir}/README.md").withPrintWriter { out ->
        out.println("# This is a test repo")
      }
      exec {
        workingDir = repoDir
        commandLine = ['git', 'add', '.']
      }
      exec {
        workingDir = repoDir
        commandLine = ['git', 'commit', '--author=GoCD Test User <devnull@example.com>', '-m', 'Initial commit']
        standardOutput = new ByteArrayOutputStream()
      }
    }
  }
}

tasks.register('copyTestDataJarsToServerDir', Copy) {
  from({ configurations.copyOnlyTestData })
  into 'testdata'
  rename "(.*)-${project.version}-.*.jar", '$1.jar'
}

tasks.register('copyAgentJarsToServerDir', Copy) {
  from({ configurations.copyOnly })
  into 'defaultFiles'
  rename "(.*)-${project.version}.jar", '$1.jar'
}

tasks.register('copyAgentSideJarsToServerDir') {
  dependsOn = [copyAgentJarsToServerDir, copyTestDataJarsToServerDir]
}

def prepareNoRails = tasks.register('prepareNoRails') {
  dependsOn = [cleanDb, ':plugin-infra:go-plugin-activator:jar', copyAgentJarsToServerDir]
}

def prepare = tasks.register('prepare') {
  dependsOn = [prepareNoRails, prepareTestGitRepo, ':server:compileAssetsWebpackDev']
}

licenseMain {
  // Add in the webapp for license checks since we do not have a dedicated sourceSet for it
  source += webAppDir
}
licenseFormatMain {
  // Add in the webapp for license checks since we do not have a dedicated sourceSet for it
  source += webAppDir
}

static def isIntegrationTest(String text) {
  text =~ /SpringExtension|SpringJUnitJupiterConfig|@ContextConfiguration/
}

test {
  description = 'Runs the fast unit tests.'

  forkEvery = 256
  maxHeapSize = '2g'

  reports {
    html.required = true
    junitXml.required = true
  }

  def testFiles = project.files([])
  def injected = project.objects.newInstance(Injected)
  doFirst {
    sourceSets[name].java.srcDirs.each { srcDir ->
      def srcFiles = injected.objectFactory.fileTree().from(srcDir)
      srcFiles.include('**/*.java')
      testFiles = testFiles.plus(srcFiles)
    }

    def badFiles = testFiles.findAll { eachFile -> isIntegrationTest(eachFile.getText("utf-8")) }
    if (!badFiles.isEmpty()) {
      throw new GradleException("${name} is not supposed to contain any integration tests. The following files contained what looks like integration test code:\n${badFiles.collect { f -> "  - ${f}" }.join("\n")}")
    }
  }
}

tasks.register('integrationTest', Test) { thisTask ->
  description = 'Runs the integration tests.'
  group = 'verification'
  testClassesDirs = sourceSets.integrationTest.output.classesDirs
  classpath = testing.suites.test.sources.runtimeClasspath + sourceSets.integrationTest.runtimeClasspath
  mustRunAfter test

  forkEvery = System.properties.containsKey('idea.home.path') ? 0 : 1
  maxHeapSize = '2g'

  reports {
    html.required = true
    junitXml.required = true
  }

  def testFiles = project.files([])
  def injected = project.objects.newInstance(Injected)
  doFirst {
    sourceSets[thisTask.name].java.srcDirs.each { srcDir ->
      def srcFiles = injected.objectFactory.fileTree().from(srcDir)
      srcFiles.include('**/*.java')
      testFiles = testFiles.plus(srcFiles)
    }

    def badFiles = testFiles.findAll { eachFile ->
      def text = eachFile.getText("utf-8")
      text.contains('@Test') && !isIntegrationTest(text)
    }
    if (!badFiles.isEmpty()) {
      throw new GradleException("${thisTask} is not supposed to contain any unit tests. The following files contained what looks like unit test code:\n${badFiles.collect { f -> "  - ${f}" }.join("\n")}")
    }
  }
}

tasks.withType(Test).configureEach {
  dependsOn copyAgentSideJarsToServerDir
  dependsOn prepareNoRails

  systemProperties += [
    'db.host'                       : 'localhost',
    'db.user'                       : 'sa',
    'db.password'                   : '',
    'db.name'                       : 'cruise',
    'db.port'                       : '',
    'rails.use.compressed.js'       : false,
    'go.enforce.server.immutability': 'N',
  ]

  testLogging {
    showStandardStreams = false
    exceptionFormat = 'full'
  }

  onOutput { TestDescriptor descriptor, TestOutputEvent event ->
    if (event.destination == TestOutputEvent.Destination.StdOut) {
      logger.quiet(TextUtils.toPlatformLineSeparators(TextUtils.indent(event.getMessage(), "  OUT: ")))
    } else if (event.destination == TestOutputEvent.Destination.StdErr) {
      logger.quiet(TextUtils.toPlatformLineSeparators(TextUtils.indent(event.getMessage(), "  ERR: ")))
    }
  }
}

check.dependsOn(integrationTest)

clean {
  delete 'artifacts'
  delete 'artifactsDir'
  delete 'data'
  delete 'felix-cache'
  delete 'pipelines'
  delete 'plugins'
  delete 'tfs-impl-14.jar'
  delete 'agent-launcher.jar'
  delete 'agent.jar'
  delete 'plugins_work'
  delete 'testdata/test-agent.jar'
  delete 'config/agentkeystore'
  delete 'config/keystore'
  delete 'config/truststore'
}

tasks.register('configureWar') {
  dependsOn findGemsToNotPack
  dependsOn ':server:compileAssets'

  inputs.properties([
    bundledGemRubyVersion: bundledGemRubyVersion
  ])
  inputs.dir(webAppDir)

  doFirst {
    war.from(webAppDir) { theSpec ->
      into '/'

      // Unnecessary dev-time configuration
      exclude('WEB-INF/web.xml')
      exclude("**/rails/.tool-versions")
      exclude("**/rails/.babel*")
      exclude("**/rails/.eslint*")
      exclude("**/rails/.stylelint*")
      exclude("**/rails/.yarnrc.yml")
      exclude("**/rails/package.json")
      exclude("**/rails/yarn.lock")
      exclude("**/rails/karma.conf.js")
      exclude("**/rails/Rakefile")
      exclude("**/rails/*.json")

      // Unrelated Rails/Ruby stuff
      exclude("**/rails/*.log")
      exclude("**/rails/log/")
      exclude("**/rails/logs/")
      exclude("**/rails/spec/")
      exclude("**/rails/tmp/")
      exclude("**/rails/yarn-license-report/")

      // Vendored pieces that are compiled and incorporated into `public` as necessary by Rails/Sprockets or Webpack
      exclude("**/rails/.yarn/")
      exclude("**/rails/node_modules/")
      exclude("**/rails/node-vendor/")
      exclude("**/rails/vendor/")

      // Rails/Sprockets-compiled or Webpacked assets
      exclude("**/rails/app/assets/images/")
      exclude("**/rails/app/assets/javascripts/")
      exclude("**/rails/app/assets/*stylesheets/")
      exclude("**/rails/webpack/")

      // Unnecessary Gems and related cruft
      exclude("**/rails/gems/jruby/${bundledGemRubyVersion}/bin/")
      exclude("**/rails/gems/jruby/${bundledGemRubyVersion}/bundler/")
      exclude("**/rails/gems/jruby/${bundledGemRubyVersion}/cache/")
      new JsonSlurper().parse(findGemsToNotPack.outputFile).each { gem ->
        theSpec.exclude("**/rails/gems/jruby/${bundledGemRubyVersion}/gems/${gem}")
        theSpec.exclude("**/rails/gems/jruby/${bundledGemRubyVersion}/specifications/${gem}.gemspec")
        theSpec.exclude("**/rails/gems/jruby/${bundledGemRubyVersion}/extensions/**/${gem}")
      }
    }
  }
}

def war = tasks.register('war', Jar) {
  dependsOn configureWar, ':server:jar'
  dependsOn configurations.runtimeClasspath, configurations.packagingOnly, configurations.allApis, configurations.spark

  finalizedBy 'verifyWar'
  archiveBaseName = 'cruise'
  archiveExtension = 'war'

  def appServerJarTask = project(':app-server').tasks.named('jar') as TaskProvider<Jar>

  def classpathResolver = {
    // Using a Set<String> is more reliable than Set<File> because File.equals() may be affected by other internal fields
    // aside from `path` that aren't important to us; thus, Set<File> may allow duplicate paths and Set<String> will not.
    Set<String> classpath = (
      configurations.runtimeClasspath
        + configurations.packagingOnly
        + configurations.allApis
        + configurations.spark
        - configurations.runtimeClasspath.findAll { it.name.startsWith('logback') } // Rely on logback from launcher
    )
      .collect { f -> f.toString() }
      .toSet()

    // exclude app-server because it causes class cast exceptions (loaded by different classloaders)
    classpath.removeAll([
      appServerJarTask.get().archiveFile.get().toString(),
      jar.archiveFile.get().toString()
    ])

    classpath
  }

  from(classpathResolver) {
    into 'WEB-INF/lib'
  }

  from(zipTree(jar.archiveFile)) {
    into('WEB-INF/classes')
  }

  from("${webAppDir}/WEB-INF/rails/webpack/rails-shared/plugin-endpoint.js") {
    into('WEB-INF/classes')
  }

  from("${project(':config:config-server').sourceSets.main.resources}/cruise-config.xsd") {
    into '/'
  }

  from(project.project(':config:config-server').sourceSets.main.resources.srcDirs) {
    into('/') include('cruise-config.xsd')
  }

  from({ compileAssets }) { into '/WEB-INF/rails/public/assets' }

  from("${webAppDir}/WEB-INF/web.xml") {
    filter {
      it.replaceAll('<param-value>development</param-value>', '<param-value>production</param-value>')
    }
    into "/WEB-INF"
  }
}

tasks.register('verifyWar', VerifyJarTask) {
  jarTask = war
  expectedJars = [
    "WEB-INF/lib": (
      ((project(':api').subprojects + project(':spark').subprojects).collect { eachProject -> eachProject.jar.archiveFile.get().asFile.name })
        + [
        "JavaEWAH-1.2.3.jar",
        "activemq-broker-${project.versions.activeMQ}.jar",
        "activemq-client-${project.versions.activeMQ}.jar",
        "activemq-openwire-legacy-${project.versions.activeMQ}.jar",
        "angus-activation-2.0.3.jar",
        "angus-core-${project.versions.angusMailSmtp}.jar",
        "animal-sniffer-annotations-1.9.jar",
        "ant-${project.versions.apacheAnt}.jar",
        "antlr-2.7.6.jar",
        "aopalliance-1.0.jar",
        "aspectjweaver-${project.versions.aspectj}.jar",
        "base-${project.version}.jar",
        "bucket4j_jdk17-core-${project.versions.bucket4j}.jar",
        "bucket4j-caffeine-${project.versions.bucket4jCaffeine}.jar",
        "caffeine-${project.versions.caffeine}.jar",
        "cloning-${project.versions.cloning}.jar",
        "commandline-${project.version}.jar",
        "common-${project.version}.jar",
        "commons-codec-${project.versions.commonsCodec}.jar",
        "commons-collections-${project.versions.commonsCollections}.jar",
        "commons-collections4-${project.versions.commonsCollections4}.jar",
        "commons-dbcp2-${project.versions.commonsDbcp}.jar",
        "commons-fileupload-${project.versions.commonsFileUpload}.jar",
        "commons-io-${project.versions.commonsIO}.jar",
        "commons-lang3-${project.versions.commonsLang3}.jar",
        "commons-text-${project.versions.commonsText}.jar",
        "commons-pool2-${project.versions.commonsPool}.jar",
        "config-api-${project.version}.jar",
        "config-server-${project.version}.jar",
        "db-${project.version}.jar",
        "db-migration-${project.version}.jar",
        "db-support-base-${project.version}.jar",
        "db-support-h2-${project.version}.jar",
        "db-support-mysql-${project.version}.jar",
        "db-support-postgresql-${project.version}.jar",
        "dom4j-${project.versions.dom4j}.jar",
        "domain-${project.version}.jar",
        "ehcache-core-${project.versions.ehcache}.jar",
        "freemarker-${project.versions.freemarker}.jar",
        "go-plugin-access-${project.version}.jar",
        "go-plugin-activator-${project.version}.jar",
        "go-plugin-api-${project.version}.jar",
        "go-plugin-api-internal-${project.version}.jar",
        "go-plugin-config-repo-${project.version}.jar",
        "go-plugin-domain-${project.version}.jar",
        "go-plugin-infra-${project.version}.jar",
        "gson-${project.versions.gson}.jar",
        "h2-${project.versions.h2}.jar",
        "hawtbuf-1.11.jar",
        "hibernate-commons-annotations-3.2.0.Final.jar",
        "hibernate-core-${project.versions.hibernate}.jar",
        "hibernate-ehcache-${project.versions.hibernate}.jar",
        "hibernate-jpa-2.0-api-1.0.1.Final.jar",
        "httpclient-${project.versions.apacheHttpComponents}.jar",
        "httpcore-4.4.16.jar",
        "httpmime-${project.versions.apacheHttpComponents}.jar",
        "istack-commons-runtime-4.1.2.jar",
        "jackson-annotations-${project.versions.jacksonBom.replaceFirst(/\.\d+$/, '')}.jar",
        "jackson-core-${project.versions.jacksonBom}.jar",
        "jackson-databind-${project.versions.jacksonBom}.jar",
        "jakarta.activation-api-2.1.4.jar",
        "jakarta.annotation-api-${project.versions.jakartaAnnotation}.jar",
        "jakarta.inject-api-${project.versions.jakartaInject}.jar",
        "jakarta.jms-api-3.1.0.jar",
        "jakarta.mail-api-${project.versions.jakartaMail}.jar",
        "jakarta.servlet-api-${project.versions.servletApi}.jar",
        "jakarta.transaction-api-${project.versions.jakartaTransaction}.jar",
        "jakarta.xml.bind-api-4.0.4.jar",
        "javassist-${project.versions.javassist}.jar",
        "jaxb-core-${project.versions.jaxbBom}.jar",
        "jaxb-runtime-${project.versions.jaxbBom}.jar",
        "jaxen-${project.versions.jaxen}.jar",
        "jcl-over-slf4j-${project.versions.slf4jBom}.jar",
        "jdom2-${project.versions.jdom}.jar",
        "jolt-core-${project.versions.jolt}.jar",
        "jruby-complete-${project.versions.jruby}.jar",
        "jruby-rack-${project.versions.rack}.jar",
        "json-utils-${project.versions.jolt}.jar",
        "liquibase-core-${project.versions.liquibase}.jar",
        "liquibase-slf4j-${project.versions.liquibaseSlf4j}.jar",
        "mybatis-${project.versions.mybatis}.jar",
        "mybatis-spring-${project.versions.mybatisSpring}.jar",
        "mysql-connector-j-${project.versions.mysql}.jar",
        "objenesis-${project.versions.objenesis}.jar",
        "org.apache.felix.framework-${project.versions.felix}.jar",
        "org.eclipse.jgit-${project.versions.jgit}.jar",
        "org.eclipse.jgit.http.server-${project.versions.jgit}.jar",
        "oscache-${project.versions.oscache}.jar",
        "plugin-metadata-store-${project.version}.jar",
        "postgresql-${project.versions.postgresql}.jar",
        "quartz-${project.versions.quartz}.jar",
        "rack_hack-${project.version}.jar",
        "semantic-version-${project.versions.semanticVersion}.jar",
        "slf4j-api-${project.versions.slf4jBom}.jar",
        "smtp-${project.versions.angusMailSmtp}.jar",
        "spark-core-${project.versions.spark}.jar",
        "spring-aop-${project.versions.spring}.jar",
        "spring-beans-${project.versions.spring}.jar",
        "spring-context-${project.versions.spring}.jar",
        "spring-context-support-${project.versions.spring}.jar",
        "spring-core-${project.versions.spring}.jar",
        "spring-expression-${project.versions.spring}.jar",
        "spring-jdbc-${project.versions.spring}.jar",
        "spring-orm-${project.versions.spring}.jar",
        "spring-security-config-${project.versions.springSecurity}.jar",
        "spring-security-core-${project.versions.springSecurity}.jar",
        "spring-security-web-${project.versions.springSecurity}.jar",
        "spring-tx-${project.versions.spring}.jar",
        "spring-web-${project.versions.spring}.jar",
        "spring-webmvc-${project.versions.spring}.jar",
        "statistics-1.0.6.jar",
        "txw2-${project.versions.jaxbBom}.jar",
        "urlrewritefilter-${project.versions.urlrewrite}.jar",
        "util-${project.version}.jar",
        "zt-exec-${project.versions.ztExec}.jar",
      ])
  ] as Map<String, List<String>>
}

tasks.register('licenseReportAggregate') {
  rootProject.subprojects.each { eachProject ->
    def reportTasks = eachProject.tasks.withType(ReportTask)
    if (!reportTasks.isEmpty()) {
      licenseReportAggregate.dependsOn reportTasks
      licenseReportAggregate.inputs.dir(eachProject.licenseReport.outputDir)
    }
  }
  dependsOn generateLicenseReport

  def licenseReportAggregateOutputDir = project.file("${layout.getBuildDirectory().get()}/reports/license-report-aggregate")
  outputs.dir(licenseReportAggregateOutputDir)

  LinkedHashSet<Map<String, Object>> missingLicenseData = [
    [
      moduleName    : 'net.sf.ehcache.internal:ehcache-core',
      moduleVersion : project.versions.ehcache,
      moduleUrls    : [
        "https://www.ehcache.org/"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Apache-2.0',
          moduleLicenseUrl: "https://spdx.org/licenses/Apache-2.0.html"
        ]
      ]
    ],
    [
      moduleName    : 'antlr:antlr',
      moduleVersion : '2.7.6',
      moduleUrls    : [
        "https://www.antlr.org"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'BSD-3-Clause',
          moduleLicenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html"
        ]
      ]
    ],
    [
      moduleName    : 'com.bazaarvoice.jolt:jolt-core',
      moduleVersion : project.versions.jolt,
      moduleUrls    : [
        "https://github.com/bazaarvoice/jolt"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Apache-2.0',
          moduleLicenseUrl: "https://spdx.org/licenses/Apache-2.0.html"
        ]
      ]
    ],
    [
      moduleName    : 'com.bazaarvoice.jolt:json-utils',
      moduleVersion : project.versions.jolt,
      moduleUrls    : [
        "https://github.com/bazaarvoice/jolt"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Apache-2.0',
          moduleLicenseUrl: "https://spdx.org/licenses/Apache-2.0.html"
        ]
      ]
    ],
    [
      moduleName    : ':com.microsoft.tfs.sdk',
      moduleVersion : project.versions.tfsSdk,
      moduleUrls    : [
        "https://github.com/Microsoft/team-explorer-everywhere"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'MIT',
          moduleLicenseUrl: "https://spdx.org/licenses/MIT.html"
        ]
      ]
    ],
    [
      moduleName    : 'org.hibernate.javax.persistence:hibernate-jpa-2.0-api',
      moduleVersion : '1.0.1.Final',
      moduleUrls    : [
        "https://hibernate.org"
      ],
      moduleLicenses: [
        [
          moduleLicense   : 'Eclipse Distribution License - v1.0',
          moduleLicenseUrl: "https://www.eclipse.org/org/documents/edl-v10.php"
        ]
      ]
    ],
    [
      moduleName    : 'jaxen:jaxen',
      moduleVersion : project.versions.jaxen,
      moduleUrls    : [
        "http://www.cafeconleche.org/jaxen/"
      ],
      moduleLicenses: [
        [
          // License is automatically detected as 0BSD by gradle-license-report, but appears to actually be BSD-3-Clause in nature
          moduleLicense   : 'BSD-3-Clause',
          moduleLicenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html"
        ]
      ]
    ],
  ]

  def goCDJarsWithoutLicenses = ['agent-launcher.jar',
                                 'agent.jar',
                                 'go-plugin-activator.jar',
                                 'tfs-impl-14.jar',
                                 'gocd-json-config-plugin.jar',
                                 'gocd-yaml-config-plugin.jar',
                                 'gocd-filebased-authentication-plugin.jar',
                                 'gocd-ldap-authentication-plugin.jar',
                                 'gocd-file-based-secrets-plugin.jar']

  def getAllJarDependencies = {
    // get a list of all dependencies from each gradle module
    def allDependenciesInReport = rootProject.subprojects.inject(new LinkedHashSet<Map<String, Object>>(), { result, eachProject ->
      if (!eachProject.hasProperty('licenseReport')) return result

      def file = eachProject.file("${eachProject.licenseReport.outputDir}/index.json")
      if (!file.exists()) return result

      def json = new JsonSlurper().parse(file) as Map<String, Object>
      if (json.dependencies != null) {
        result.addAll(json.dependencies)
      }
      return result
    })

    // remove items that we know to have missing license data
    allDependenciesInReport.removeIf { eachDependency ->
      missingLicenseData.any { dependencyWithMissingLicenseData ->
        eachDependency.moduleName == dependencyWithMissingLicenseData.moduleName && eachDependency.moduleVersion == dependencyWithMissingLicenseData.moduleVersion
      }
    }

    // then add the missing license data
    allDependenciesInReport.addAll(missingLicenseData)

    allDependenciesInReport
  }

  def getLicenseDataFor = { Set<String> allJars, allDependenciesInReport ->
    allJars.inject([:], { Map<String, Map<String, Object>> result, String eachPackagedJar ->
      Map<String, Object> module = allDependenciesInReport.find { Map<String, Object> eachDependencyInReport ->
        def (_, artifactId) = eachDependencyInReport.moduleName.split(':')
        def version = eachDependencyInReport.moduleVersion
        eachPackagedJar == "${artifactId}-${version}.jar"
      }

      if (module == null) {
        throw new GradleException("Expected to find packaged jar ${eachPackagedJar} but was not found in the license report")
      }

      result[module.moduleName] = module
      result
    })
  }

  def injected = project.objects.newInstance(Injected)
  doFirst {
    LinkedHashSet<Map<String, Object>> allDependenciesInReport = getAllJarDependencies()

    List<VerifyJarTask> verifyJarTasks = rootProject.subprojects.collect { eachProject ->
      eachProject.tasks.withType(VerifyJarTask)
    }.flatten()

    // list of all jars that we package
    Set<String> allJars = verifyJarTasks.collect { VerifyJarTask eachTask ->
      eachTask.expectedJars.values().flatten()
    }.flatten().collect { name -> name.toString() }.sort().unique()

    // remove unwanted stuff, mostly nested zip files and jars with GoCD package version
    allJars.removeIf { name -> name.endsWith(".zip") || name.endsWith("-${version}.jar") || name.contains("-${version}-") }
    allJars.removeAll(goCDJarsWithoutLicenses)

    // get the licenses corresponding to the jars we actually package.
    Map<String, Map<String, Object>> licensesForPackagedJarDependencies = getLicenseDataFor(allJars, allDependenciesInReport)

    injected.fileOps.delete { delete(licenseReportAggregateOutputDir) }
    licenseReportAggregateOutputDir.mkdirs()

    def licenseReport = new LicenseReport(project, licenseReportAggregateOutputDir, licensesForPackagedJarDependencies, compileAssetsWebpackProd.ext.yarnLicenseReportFile, generateRubygemsLicenseReport.ext.licenseReportFile)
    licenseReport.generate()
  }
}

apply from: 'rails.gradle'
apply from: 'jasmine.gradle'
apply from: 'helpers.gradle'
apply from: 'lint.gradle'

generateLicenseReport.dependsOn(compileAssetsWebpackProd, generateRubygemsLicenseReport)

tasks.register('verifyJar') {
  dependsOn verifyWar
}
