// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// 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 org.gradle.api.internal.tasks.userinput.UserInputHandler

plugins {
  id "org.flywaydb.flyway" version "6.0.1"
  id 'maven-publish'
}

ext {
  Set restrictedDbEnv =
      [ 'sandbox', 'production' ].asUnmodifiable()
  Set allDbEnv =
      [ 'alpha', 'crash' ].plus(restrictedDbEnv).asUnmodifiable()

  def dbServerProperty = 'dbServer'
  def dbNameProperty = 'dbName'

  def dbServer = findProperty(dbServerProperty).toString().toLowerCase()
  def dbName = findProperty(dbNameProperty)

  isCloudSql = {
    return allDbEnv.contains(dbServer)
  }

  getAccessInfoByHostPort = { hostAndPort ->
    return [
        url: "jdbc:postgresql://${hostAndPort}/${dbName}",
        user: findProperty('dbUser'),
        password: findProperty('dbPassword')]
  }

  getSocketFactoryAccessInfo = { env ->
    def cred = getCloudSqlCredential(env, 'admin').split(' ')
    def sqlInstance = cred[0]
    return [
        url: """\
                   jdbc:postgresql://google/${dbName}?cloudSqlInstance=
                   ${sqlInstance}&socketFactory=
                   com.google.cloud.sql.postgres.SocketFactory"""
                   .stripIndent()
                   .replaceAll(System.lineSeparator(), '') ,
        user: cred[1],
        password: cred[2]]
  }

  getJdbcAccessInfo = {
    if (allDbEnv.contains(dbServer)) {
      return getSocketFactoryAccessInfo(dbServer)
    } else {
      return getAccessInfoByHostPort(dbServer)
    }
  }

  // Retrieves Cloud SQL credential for a given role. Result is in the form of
  // 'instancename username password'.
  //
  // The env parameter may be one of the following: alpha, crash, sandbox, or
  // production. The role parameter may be superuser. (More roles will be added
  // later).
  getCloudSqlCredential = { env, role ->
    def devProject = project.hasProperty('devProject')
        ? project.getProperty('devProject') : rootProject.devProject
    def command =
        """gsutil cp \
           gs://${devProject}-deploy/cloudsql-credentials/${env}/${role}_credential.enc - | \
           base64 -d | \
           gcloud kms decrypt --location global --keyring nomulus-tool-keyring \
           --key nomulus-tool-key --plaintext-file=- \
           --ciphertext-file=- \
           --project=${devProject}"""

    return execInBash(command, '/tmp')
  }
}

task schemaJar(type: Jar) {
  archiveBaseName = 'schema'
  from(sourceSets.main.resources) {
    include 'sql/flyway/**'
    include 'sql/schema/nomulus.golden.sql'
  }
}

// Expose NomulusPostgreSql class to ':core' for compile, without leaking
// unnecessary dependencies to the release artifacts through ':core'.
// Jar is put in the 'compileApi' configuration.
task compileApiJar(type: Jar) {
  archiveBaseName = 'compile'
  from(sourceSets.main.output) {
    include 'google/registry/persistence/NomulusPostgreSql**'
  }
}

configurations {
  compileApi
  schema
}

artifacts {
  compileApi compileApiJar
  schema schemaJar
}

publishing {
  repositories {
    maven {
      url project.publish_repo
    }
  }
  publications {
    sqlSchemaPublication(MavenPublication) {
      groupId 'google.registry'
      artifactId 'schema'
      version project.schema_version
      artifact schemaJar
    }
  }
}

flyway {
  def accessInfo = project.ext.getJdbcAccessInfo()

  url = accessInfo.url
  user = accessInfo.user
  password = accessInfo.password
  schemas = [ 'public' ]

  locations = [ "classpath:sql/flyway" ]
}

dependencies {
  def deps = rootProject.dependencyMap

  compile deps['org.flywaydb:flyway-core']

  runtimeOnly deps['com.google.cloud.sql:postgres-socket-factory']
  runtimeOnly deps['org.postgresql:postgresql']

  testCompile deps['com.google.flogger:flogger']
  testRuntime deps['com.google.flogger:flogger-system-backend']
  testCompile deps['com.google.guava:guava']
  testCompile deps['com.google.truth:truth']
  testRuntime deps['io.github.java-diff-utils:java-diff-utils']
  testCompile deps['junit:junit']
  testCompile deps['org.testcontainers:postgresql']
  testCompile deps['org.testcontainers:testcontainers']
  testCompile project(path: ':common', configuration: 'testing')
  testCompile project(':third_party')
}

flywayInfo.dependsOn('buildNeeded')
flywayValidate.dependsOn('buildNeeded')

if (ext.isCloudSql()) {
  // Disable dangerous Flyway tasks. Only allow info and validate.
  tasks.findAll { task -> task.group.equals('Flyway')}.each {
    if (it.name == 'flywayMigrate') {
      it.doFirst {
        throw new UnsupportedOperationException(
            """ \
              FlywayMigrate is disabled. See README.md for schema deployment
              instructions.""".stripIndent())
      }
    } else if (it.name != 'flywayInfo' && it.name != 'flywayValidate') {
      it.doFirst {
        throw new UnsupportedOperationException(
            "${it.name} from commandline is not allowed.")
      }
    }
  }
}
