import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

import java.time.Duration

plugins {
  alias(libs.plugins.spotless)
  alias(libs.plugins.docker)
}

task compileAll

// to allow to have unused vars/imports,etc for faster debugging/prototyping
// instead of deleting and re-adding code all the time
def allowCompilationWarnings = System.getenv('LINEA_DEV_ALLOW_WARNINGS') != null

allprojects {
  repositories {
    mavenCentral()
    mavenLocal()
  }

  apply plugin: 'java' // do not add kotlin plugin here, it will add unnecessary Kotlin runtime dependencies
  apply plugin: 'jacoco'

  tasks.withType(KotlinCompile).configureEach {
    compileAll.dependsOn it
    compilerOptions {
      allWarningsAsErrors = !allowCompilationWarnings
    }
  }

  tasks.withType(JavaCompile).configureEach {
    compileAll.dependsOn it
    options.encoding = 'UTF-8'
    options.deprecation = true
    options.compilerArgs.addAll([
      '-parameters',
      '-Xlint:cast',
      '-Xlint:overloads',
      '-Xlint:divzero',
      '-Xlint:finally',
      '-Xlint:static',
      '-Xlint:deprecation',
    ])
    if (!allowCompilationWarnings) {
      options.compilerArgs.addAll(['-Werror'])
    }

    if (!project.path.contains("testing-tools")) {
      // testing tools have 100+ errors because of this
      // skipping them for now
      options.compilerArgs.addAll(['-Xlint:rawtypes'])
    }
  }

  jacoco {
    toolVersion = '0.8.11'
    if (project.tasks.findByName('integrationTest')) {
      applyTo integrationTest
    }
    if (project.tasks.findByName('acceptanceTest')) {
      applyTo acceptanceTest
    }
  }

  jacocoTestReport {
    dependsOn test
  }

  tasks.withType(Test).configureEach {
    testLogging {
      events = [
        //TestLogEvent.STARTED,
        //TestLogEvent.PASSED,
        TestLogEvent.FAILED,
        TestLogEvent.SKIPPED,
        TestLogEvent.STANDARD_ERROR
      ]
      exceptionFormat TestExceptionFormat.FULL
      showCauses true
      showExceptions true
      showStackTraces true
      // set showStandardStreams if you need to see test logs
      showStandardStreams false
    }
    systemProperty("L1_RPC_URL", "http://localhost:8445")
    systemProperty("L2_RPC_URL", "http://localhost:8545")
    systemProperty("L1_GENESIS", "docker/config/l1-node/el/genesis.json")
    systemProperty("L2_GENESIS", "docker/config/linea-local-dev-genesis-PoA-besu.json")

    systemProperties["junit.jupiter.execution.timeout.default"] = "5 m" // 5 minutes
    systemProperties["junit.jupiter.execution.parallel.enabled"] = true
    systemProperties["junit.jupiter.execution.parallel.mode.default"] = "concurrent"
    systemProperties["junit.jupiter.execution.parallel.mode.classes.default"] = "concurrent"
    maxParallelForks = Math.max(Runtime.runtime.availableProcessors(), 9)
  }

  tasks.withType(Test).matching { it.name == 'integrationTest' }.configureEach {
    outputs.cacheIf { false }
    outputs.upToDateWhen { false }
  }

  afterEvaluate { subproject ->
    if (hasJavaOrKotlinPlugins(subproject)) {
      subproject.apply plugin: 'com.diffplug.spotless'

      subproject.spotless {
        if (hasKotlinPlugin(subproject)) {
          kotlin {
            // by default the target is every '.kt' and '.kts` file in the java sourcesets
            //ktfmt()
            ktlint(libs.versions.ktlint.get().toString()).setEditorConfigPath("$rootDir/.editorconfig")
          }
        }
        // spotless check applied to build.gradle (groovy) files
        groovyGradle {
          target '*.gradle'
          greclipse()
          indentWithSpaces(2)
          endWithNewline()
        }

        java {
          target 'src/**/*.java'
          // Do not use 'targetExclude' with wildcard patterns, it will add minutes to the build
          removeUnusedImports()
          trimTrailingWhitespace()
          endWithNewline()
          googleJavaFormat(libs.versions.googleJavaFormat.get())
        }
      }
    }
  }
}

task jacocoRootReport(type: JacocoReport) {
  additionalSourceDirs.from files(subprojects.sourceSets.main.allSource.srcDirs)
  sourceDirectories.from files(subprojects.sourceSets.main.allSource.srcDirs)
  classDirectories.from files(subprojects.sourceSets.main.output)

  executionData.from fileTree(dir: '.', includes: ['**/jacoco/*.exec'])
  reports {
    xml.required = true
    // xml.enabled = true FIXME: deprecated, breaking latest versions of gradle.
    csv.required = true
    html.destination file("build/reports/jacocoHtml")
  }
  onlyIf = { true }
}

dockerCompose {
  localStack {
    startedServices = [
      "postgres",
      "sequencer",
      "maru",
      "l1-node-genesis-generator",
      "l2-genesis-initialization",
      "l1-el-node",
      "l1-cl-node",
      // For debug
      // "l1-blockscout",
      // "l2-blockscout"
    ]
    composeAdditionalArgs = [
      "--profile",
      "l1",
      "--profile",
      "l2"
    ]
    useComposeFiles = [
      "${project.rootDir.path}/docker/compose-tracing-v2.yml"
    ]
    waitForHealthyStateTimeout = Duration.ofMinutes(3)
    waitForTcpPorts = false
    stopContainers = false
    removeOrphans = true
    // this is to avoid recreating the containers
    // specially l1-node-genesis-generator which corrupts the state if run more than once
    // without cleaning the volumes
    noRecreate = true
    projectName = "docker"
  }

  localStackPostgresDbOnly {
    startedServices = ["postgres"]
    useComposeFiles = [
      "${project.rootDir.path}/docker/compose-tracing-v2.yml"
    ]
    waitForHealthyStateTimeout = Duration.ofMinutes(3)
    stopContainers = false
    waitForTcpPorts = true
    removeOrphans = true
    noRecreate = true
    projectName = "docker"
  }

  localStackForStateRecovery {
    startedServices = [
      "postgres",
      "sequencer",
      "l1-node-genesis-generator",
      "l1-el-node",
      "l1-cl-node",
      "blobscan-api",
      "blobscan-indexer",
      "redis",
    ]
    composeAdditionalArgs = [
      "--profile",
      "l1",
      "--profile",
      "l2",
      "--profile",
      "staterecovery"
    ]
    useComposeFiles = [
      "${project.rootDir.path}/docker/compose-tracing-v2-staterecovery-extension.yml"
    ]
    waitForHealthyStateTimeout = Duration.ofMinutes(3)
    waitForTcpPorts = false
    removeOrphans = true
    stopContainers = false
    // this is to avoid recreating the containers
    // specially l1-node-genesis-generator which corrupts the state if run more than once
    // without cleaning the volumes
    noRecreate = true
    projectName = "docker"
  }
}

static Boolean hasKotlinPlugin(Project proj) {
  return proj.plugins.hasPlugin("org.jetbrains.kotlin.jvm")
}

static Boolean hasJavaPlugin(Project proj) {
  return (proj.plugins.hasPlugin("java") || proj.plugins.hasPlugin("java-library"))
}

static Boolean hasJavaOrKotlinPlugins(Project proj) {
  return (hasKotlinPlugin(proj) || hasJavaPlugin(proj))
}
