/*
 * Copyright 2020 Google LLC
 *
 * 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
 *
 *      https://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.
 */

// This module handles Play Store pushes for EN Express apps.

apply from: 'enexpress_locales.gradle'

buildscript {
  repositories {
    google()
    mavenCentral()
    jcenter()
  }
  dependencies {
    classpath "com.google.apis:google-api-services-androidpublisher:v3-rev142-1.25.0"
    classpath "com.google.api-client:google-api-client-jackson2:1.20.0"
    classpath "com.google.auth:google-auth-library-oauth2-http:0.21.1"
  }
}

import com.google.api.client.googleapis.GoogleUtils
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.http.FileContent
import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpRequestInitializer
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.androidpublisher.AndroidPublisher
import com.google.api.services.androidpublisher.AndroidPublisher.Edits
import com.google.api.services.androidpublisher.AndroidPublisherScopes
import com.google.api.services.androidpublisher.model.Apk
import com.google.api.services.androidpublisher.model.AppEdit
import com.google.api.services.androidpublisher.model.Listing
import com.google.api.services.androidpublisher.model.LocalizedText
import com.google.api.services.androidpublisher.model.Track
import com.google.api.services.androidpublisher.model.TrackRelease


// Add 2 minute timeouts to http requests
// Required for Publish API. See:
// https://developers.google.com/resources/api-libraries/documentation/androidpublisher/v3/java/latest/com/google/api/services/androidpublisher/AndroidPublisher.Edits.Bundles.Upload.html
def setHttpTimeout(HttpRequestInitializer requestInitializer) {
  return new HttpRequestInitializer() {
    void initialize(HttpRequest httpRequest) throws IOException {
      requestInitializer.initialize(httpRequest);
      httpRequest.setConnectTimeout(2 * 60000);
      httpRequest.setReadTimeout(2 * 60000);
    }
  }
}

def login(config) {
  def serviceAccountEmail = config["serviceAccount"]
  def serviceAccountKey = fileTree("configs").findAll
     { it.getName() == config["name"] + ".p12" }[0]

  def jsonFactory = JacksonFactory.getDefaultInstance()
  NetHttpTransport httpTransport = null
  if (System.getenv("PLAY_STORE_PROXY")) {
    def proxy = System.getenv("PLAY_STORE_PROXY").split(":")
    NetHttpTransport.Builder builder = new NetHttpTransport.Builder()
    builder.trustCertificates(GoogleUtils.getCertificateTrustStore())
    builder.setProxy(new Proxy(Proxy.Type.HTTP,
                     new InetSocketAddress(proxy[0], proxy[1].toInteger())))
    httpTransport = builder.build()
  } else {
    httpTransport = GoogleNetHttpTransport.newTrustedTransport()
  }
  // Get acount credential.
  def rawCredential = new GoogleCredential.Builder()
     .setTransport(httpTransport)
     .setJsonFactory(jsonFactory)
     .setServiceAccountId(serviceAccountEmail)
     .setServiceAccountScopes(
          Collections.singleton(
              AndroidPublisherScopes.ANDROIDPUBLISHER))
     .setServiceAccountPrivateKeyFromP12File(serviceAccountKey)
     .build()
  def credential = setHttpTimeout(rawCredential)
  // Create the API service.
  def service = new AndroidPublisher.Builder(
      httpTransport, jsonFactory, credential)
      .setApplicationName(name)
      .build()
  return service
}

static def fullPackageName(config, appVersion) {
  String packageName = config["healthAuthorityID"]
  if (appVersion == 'V3') {
    packageName += "." + appVersion.toLowerCase()
    packageName = config.get("v3ApplicationIdOverride", packageName)
  }
  return packageName
}

def updateListing(packageName, config, edits, editId) {
  def locales = getConfigLocales(config)
  def defaultLocale = pickDefaultLocale(config, locales)

  def playLocales = filterLocalesForPlayStore(locales)
  playLocales.each { playLocale, plainLocale ->
    def locale = plainLocale.toUpperCase().replace("-", "_")
    def listing = new Listing()
    listing.setTitle(
        config.get("playStoreAppTitle_" + locale,
        config.get("appTitle_" + locale,
        config.get("agencyDisplayName_" + locale, ""))))
    listing.setShortDescription(
        config.get("playStoreShortDescription_" + locale,
        config.get("agencyDisplayName_" + locale, "")))
    listing.setFullDescription(
        config.get("playStoreFullDescription_" + locale,
        config.get("agencyDisplayName_" + locale, "")))
    edits.listings()
        .update(packageName, editId, playLocale, listing)
        .execute()
  }
}

static def updateTrack(packageName, config, edits, editId, bundleVersionCodes) {
  def trackRelease = new TrackRelease()
      .setVersionCodes(bundleVersionCodes)
      .setStatus("draft")  // completed
      .setReleaseNotes(Collections.singletonList(
                       new LocalizedText()
                       .setLanguage(Locale.US.toString())
                       .setText("Bug fixes and performance improvements.")))
  def track = new Track()
      .setReleases([trackRelease])
  edits.tracks()
       .update(packageName, editId, "internal", track)
       .execute()
}

static def uploadApp(packageName, config, edits, editId, appPath) {
  def file =
      new FileContent("application/octet-stream", new File(appPath))
  def app = edits
      .bundles()
      .upload(packageName, editId, file)
      .execute()
  List<Long> versionCodes = new ArrayList<>()
  versionCodes.add(Long.valueOf(app.getVersionCode()))
  return versionCodes
}

def generatePlayStorePublishApp(configData) {
  def appVersions = ["V2", "V3"]
  appVersions.each { appVersion ->
    configData.each { name, config ->
      def packageName = fullPackageName(config, appVersion)
      def appPath = "$buildDir/outputs/bundle/" + name +
          appVersion + "Release/app-" + name + "-" +
          appVersion.toLowerCase() + "-release.aab"
      def taskName = "publishApp" + name.capitalize() + appVersion
      task "$taskName" {
        inputs.files(config["files"])
        doLast {
          // Create a new edit to make changes.
          def edits = login(config).edits()
          String editId = edits.insert(packageName, null).execute().getId()

          // Upload bundle, and tracks.
          def versions = uploadApp(packageName, config, edits, editId, appPath)
          updateTrack(packageName, config, edits, editId, versions)

          // Commit changes for edit.
          edits.commit(packageName, editId).execute()
        }
      }
    }
  }
}

def generatePlayStorePublishListing(configData) {
  def appVersions = ["V2", "V3"]
  appVersions.each { appVersion ->
    configData.each { name, config ->
      def packageName = fullPackageName(config, appVersion)
      def taskName = "publishListing" + name.capitalize() + appVersion
      task "$taskName" {
        inputs.files(config["files"])
        doLast {
          // Create a new edit to make changes.
          def edits = login(config).edits()
          String editId = edits.insert(packageName, null).execute().getId()

          // Upload listing only.
          updateListing(packageName, config, edits, editId)

          // Commit changes for edit.
          edits.commit(packageName, editId).execute()
        }
      }
    }
  }
}

def generatePlayStorePublish(configData) {
  generatePlayStorePublishApp(configData)
  generatePlayStorePublishListing(configData)
}

ext {
  generatePlayStorePublish = this.&generatePlayStorePublish
}
