/*
 * 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.thoughtworks.go.build.*
import groovy.text.SimpleTemplateEngine
import groovy.text.Template
import org.apache.tools.ant.types.Commandline
import org.gradle.internal.jvm.Jvm

def jrubyScriptsDir = project.file("scripts")
def bundledGemDir = file("${project.railsRoot}/gems/jruby/${JRuby.bundledGemRubyVersion}")
def additionalJRubyPaths = [jrubyScriptsDir, project.file("${bundledGemDir}/bin")]
def jrubyEnvironment = [GEM_HOME: bundledGemDir, GEM_PATH: bundledGemDir]
project.ext.additionalJRubyPaths = additionalJRubyPaths
project.ext.jrubyEnvironment = jrubyEnvironment

def createJRubyBinstubs = tasks.register('createJRubyBinstubs') {
  group = LifecycleBasePlugin.BUILD_GROUP

  def ext = org.gradle.internal.os.OperatingSystem.current().isWindows() ? '.bat' : ''
  def outputScriptFile = file("${jrubyScriptsDir}/jruby${ext}")
  def templateProperties = [
    mainClassName       : 'org.jruby.Main',
    jrubyJar            : JRuby.jar,
    jvmArgs             : JRuby.jrubyJvmArgs,
    systemProperties    : JRuby.jrubySystemProperties,
    environment         : jrubyEnvironment,
    additionalJRubyPaths: additionalJRubyPaths,
    javaExecutable      : Jvm.current().getExecutable("java")
  ]

  inputs.dir("script-templates")
  inputs.properties(templateProperties)
  outputs.files(outputScriptFile)
  outputs.cacheIf { true }

  doFirst {
    jrubyScriptsDir.mkdirs()

    def inputTemplate = file("script-templates/jruby${ext}")
    def engine = new SimpleTemplateEngine()
    Template template = engine.createTemplate(inputTemplate)
    outputScriptFile.write(template.make(templateProperties).toString())
    outputScriptFile.setExecutable(true)
  }
}

def initializeRailsGems = tasks.register('initializeRailsGems', JRuby) {
  group = LifecycleBasePlugin.BUILD_GROUP
  outputs.cacheIf { true }
  dependsOn createJRubyBinstubs

  def root = project.railsRoot
  inputs.file("${root}/Gemfile")
  inputs.file("${root}/Gemfile.lock")
  inputs.file("${root}/.bundle/config")
  def jrubyScriptFile = createJRubyBinstubs.map { it.outputs.files.singleFile }
  inputs.file(jrubyScriptFile)
  inputs.properties([
    jrubyVersion : project.deps.jruby,
    bundledGemDir: bundledGemDir,
    railsRoot    : root
  ])

  outputs.dir(bundledGemDir)

  workingDir = root
  args = ['-S', 'bundle', 'install']
  maxHeapSize = '256m'
  environment += [
    // RUBY env var needs to be forward-slashed on Windows to avoid backslashes getting stripped somewhere
    RUBY: jrubyScriptFile.get().toString().replaceAll('\\\\', '/'),
    // Avoid issues jar-dependencies and running via jruby-complete https://github.com/jruby/jruby/issues/8886
    JARS_SKIP: 'true',
  ]
}

tasks.register('findGemsToNotPack', JRuby) {
  dependsOn initializeRailsGems

  def gemfile = "${project.railsRoot}/Gemfile"
  inputs.file(gemfile)
  inputs.file("${gemfile}.lock")
  inputs.file("find-gems-to-package.rake")
  ext.outputFile = file("${layout.getBuildDirectory().get()}/gems-to-not-package.json")
  outputs.file(outputFile)
  outputs.cacheIf { true }

  environment += [
    'OUTPUT_FILE'   : outputFile,
    'BUNDLE_GEMFILE': file(gemfile)
  ]
  args = ['-S', 'rake', '--trace', '--rakefile', 'find-gems-to-package.rake']
}

tasks.register('generateRubygemsLicenseReport', JRuby) {
  dependsOn initializeRailsGems

  def gemfile = "${project.railsRoot}/Gemfile"
  inputs.file(gemfile)
  inputs.file("${gemfile}.lock")
  inputs.file("rubygems-license-report.rake")
  ext.licenseReportFile = file("${layout.getBuildDirectory().get()}/rubygems-license.json")
  outputs.file(ext.licenseReportFile)
  outputs.cacheIf { true }

  environment += [
    'OUTPUT_FILE'   : licenseReportFile,
    'BUNDLE_GEMFILE': file(gemfile)
  ]
  args = ['-S', 'rake', '--trace', '--rakefile', 'rubygems-license-report.rake']
}

def yarnInstall = tasks.register('yarnInstall', YarnInstallTask) {
  workingDir = project.railsRoot
}

prepare.dependsOn(initializeRailsGems, yarnInstall)

tasks.register('cleanRails', Delete) {
  delete layout.buildDirectory.dir("railsTests")
  delete "${project.railsRoot}/config/cipher"
  delete "${project.railsRoot}/config/cipher.aes"
  delete "${project.railsRoot}/db/config.git"
  delete "${project.railsRoot}/db/h2db"
  delete "${project.railsRoot}/logs"
  delete "${project.railsRoot}/node_modules"
  delete "${project.railsRoot}/public/assets"
  delete "${project.railsRoot}/tmp"
  delete "${project.railsRoot}/webpack/gen"
  delete "${project.railsRoot}/yarn-error.log"
  delete "${project.railsRoot}/yarn-license-report"

  // these are purposely added as `doFirst` to avoid intellij from excluding these from the modules
  doFirst {
    delete jrubyScriptsDir
    delete bundledGemDir
  }
}
clean.dependsOn cleanRails

def generateJSRoutes = tasks.register('generateJSRoutes', ExecuteUnderRailsTask) {
  group = LifecycleBasePlugin.BUILD_GROUP

  dependsOn configurations.railsRuntimeClasspath
  classpath(configurations.railsRuntimeClasspath)

  def outputDir = file("${project.railsRoot}/webpack/gen")
  inputs.dir(file("${project.railsRoot}/vendor"))
  inputs.files(fileTree(dir: "${project.railsRoot}/config", includes: ['**/*.rb']))
  outputs.dir(outputDir)
  outputs.cacheIf { true }

  environment(
    'RAILS_ENV': 'production',
    'RAILS_GROUPS': 'assets',
    'OUTPUT_DIR': outputDir,
  )

  args = ['-S', 'rake', '--trace', 'generated_js']

  doFirst {
    delete outputDir
  }
}

tasks.register('compileAssetsWebpackDev', YarnRunTask) {
  dependsOn yarnInstall, generateJSRoutes
  mustRunAfter compileAssetsRailsTest // Both output into the same /public/assets dir :-(

  ext.yarnLicenseReportFile = layout.buildDirectory.file("reports/yarn-license/license-report.json").get().asFile
  outputs.dir(ext.yarnLicenseReportFile.parentFile)

  source(project.file("${project.railsRoot}/.babelrc"))
  source(project.file("${project.railsRoot}/tsconfig.json"))
  source(project.file("${project.railsRoot}/webpack"))
  source(project.file("${project.railsRoot}/vendor/assets"))
  source(project.file("${project.railsRoot}/config"))

  workingDir = project.railsRoot
  destinationDir = project.file("${project.railsRoot}/public/assets/webpack")
  yarnCommand = ['webpack-dev', '--env', "outputDir=${destinationDir.toPath().toString()}", '--env', "licenseReportFile=${ext.yarnLicenseReportFile}"]

  doFirst {
    delete "${workingDir}/tmp" // Clean the cacheDir from webpack/config/webpack.config.ts
  }
}

tasks.register('compileAssetsWebpackProd', YarnRunTask) {
  dependsOn yarnInstall, generateJSRoutes

  ext.yarnLicenseReportFile = layout.buildDirectory.file("reports/yarn-license/license-report.json").get().asFile
  outputs.dir(ext.yarnLicenseReportFile.parentFile)

  source(project.file("${project.railsRoot}/.babelrc"))
  source(project.file("${project.railsRoot}/tsconfig.json"))
  source(project.file("${project.railsRoot}/webpack"))
  source(project.file("${project.railsRoot}/vendor/assets"))
  source(project.file("${project.railsRoot}/config"))

  workingDir = project.railsRoot
  destinationDir = layout.buildDirectory.dir("webpack-assets").get().asFile
  yarnCommand = ['webpack-prod', '--env', "outputDir=${destinationDir.toPath().toString()}", '--env', "licenseReportFile=${ext.yarnLicenseReportFile}"]

  doFirst {
    delete "${workingDir}/tmp" // Clean the cacheDir from webpack/config/webpack.config.ts
  }
}

def compileAssetsRailsTest = tasks.register('compileAssetsRailsTest', ExecuteUnderRailsTask) {
  dependsOn yarnInstall, configurations.railsRuntimeClasspath
  classpath(configurations.railsRuntimeClasspath)

  outputs.cacheIf { true }

  def publicAssetsDir = file("${project.railsRoot}/public/assets")

  inputs.dir(file("${project.railsRoot}/config"))
  inputs.dir(file("${project.railsRoot}/app/assets"))
  inputs.dir(file("${project.railsRoot}/vendor/assets"))

  outputs.dir(publicAssetsDir)

  environment += [
    'RAILS_ENV'   : 'test',
    'RAILS_GROUPS': 'assets',
  ]

  args = ['-S', 'rake', '--trace', 'assets:clobber', 'assets:precompile']

  doFirst {
    delete "${workingDir}/tmp"
    delete publicAssetsDir
    publicAssetsDir.parentFile.mkdirs()
  }
}

tasks.register('compileAssetsRailsProd', ExecuteUnderRailsTask) {
  dependsOn yarnInstall, configurations.railsRuntimeClasspath
  classpath(configurations.railsRuntimeClasspath)
  outputs.cacheIf { true }

  def outputAssetsDir = layout.buildDirectory.dir("rails-assets")
  def publicAssetsDir = layout.buildDirectory.dir("public/assets")

  inputs.dir(file("${project.railsRoot}/config"))
  inputs.dir(file("${project.railsRoot}/app/assets"))
  inputs.dir(file("${project.railsRoot}/vendor/assets"))

  outputs.dir(outputAssetsDir)

  environment(
    'RAILS_ENV': 'production',
    'RAILS_GROUPS': 'assets',
  )

  args = ['-S', 'rake', '--trace', 'assets:clobber', 'assets:precompile']

  def injected = project.objects.newInstance(Injected)
  doFirst {
    delete "${workingDir}/tmp"
    delete outputAssetsDir
    outputAssetsDir.get().asFile.parentFile.mkdirs()
  }

  doLast {
    injected.fileOps.copy {
      from publicAssetsDir
      into outputAssetsDir
    }

    delete publicAssetsDir
  }
}

tasks.register('compileAssets') {
  dependsOn compileAssetsWebpackProd
  dependsOn licenseReportAggregate
  dependsOn compileAssetsRailsProd
  inputs.files licenseReportAggregate.outputs.files
  inputs.files compileAssetsRailsProd.outputs.files
  inputs.files compileAssetsWebpackProd.outputs.files

  def outputDir = layout.buildDirectory.dir("assets").get().asFile
  outputs.dir(outputDir)

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

    injected.fileOps.copy {
      from licenseReportAggregate
      // assets are cached with a long lived cache header
      // we purposefully add a version string in the path so every new build will basically use a different url
      into "${outputDir}/dependency-license-report-${version}"
    }
    injected.fileOps.copy {
      from compileAssetsRailsProd
      into outputDir
    }
    injected.fileOps.copy {
      from compileAssetsWebpackProd
      into "${outputDir}/webpack"
    }
  }
}

tasks.register('rspec', ExecuteUnderRailsTask) {
  dependsOn ':server:cleanDb', compileAssetsRailsTest, configurations.railsTestRuntimeClasspath
  classpath(configurations.railsTestRuntimeClasspath, rootProject.findProject(':test:test-utils').files("resource-include-in-all-projects"))
  mustRunAfter compileAssetsWebpackDev // Both this and compileAssetsRailsTest output into the same /public/assets dir :-(

  description = "Run specs (pass '-Popts=...' to pass options)"

  inputs.files(bundledGemDir)
  inputs.files("${project.railsRoot}/app")
  inputs.files("${project.railsRoot}/config")
  inputs.files("${project.railsRoot}/lib")
  inputs.files("${project.railsRoot}/spec")
  inputs.files(compileAssetsRailsTest)

  maxHeapSize = '256m'
  jvmArgs += (InstallerType.server.jvmInternalAccessArgs - jvmArgs) // Add additional args not already needed by JRuby alone
  args = ['-S', 'rspec', '--backtrace']
  if (project.hasProperty('opts')) {
    args += Commandline.translateCommandline(project.property('opts') as String)
  }
  environment += [
    'REPORTS_DIR': "${layout.buildDirectory.get()}/rspec-results"
  ]

  def testDataDir = layout.buildDirectory.dir('railsTests')
  def testConfigDir = testDataDir.map { it.dir('config') }
  systemProperties += [
    'cruise.config.dir'             : testConfigDir.get().asFile,
    'plugins.all.zip.path'          : testDataDir.map { it.file('plugins.zip') }.get().asFile,
    'rails.use.compressed.js'       : false,
    'go.enforce.server.immutability': 'N',
  ]

  def injected = project.objects.newInstance(Injected)
  doFirst {
    injected.fileOps.delete {  delete(testDataDir) }
    injected.fileOps.copy {
      from 'config'
      into testConfigDir
    }
  }
}

tasks.register('railsExec', ExecuteUnderRailsTask) {
  outputs.upToDateWhen { false }

  description = "rails exec (pass '-Pcmd=...' to pass options)"

  args = Commandline.translateCommandline(project.properties.get('cmd') as String)
}
