before_all do
  skip_docs()
  # Ensure that build and profiles directories are present in fastlane directory.
  sh("mkdir -p ./build/ ./profiles/")
end

lane :lint do
  Dir.chdir("../yivi_core") do
    sh("dart", "format", "--set-exit-if-changed", "lib/", "test/")
    sh("flutter", "analyze", "--no-fatal-infos")

    # gofmt does not return non-zero exit codes on failure, so we have to check the output.
    fmt_output = sh("gofmt", "-d", "-e", ".")
    if !fmt_output.empty?
      raise "gofmt issues found"
    end
    sh("go", "vet", "./...")
  end

  Dir.chdir("../yivi_app") do
    sh("dart", "format", "--set-exit-if-changed", "lib/", "integration_test/")
    sh("flutter", "analyze", "--no-fatal-infos")
  end

  Dir.chdir("../yivi_fdroid") do
    sh("dart", "format", "--set-exit-if-changed", "lib/")
    sh("flutter", "analyze", "--no-fatal-infos")
  end
end

lane :unit_test do
  Dir.chdir("../yivi_core") do
    sh("flutter", "test")
  end
end

lane :android_build do |options|
  android_build_irmagobridge()
  android_build_appbundle(
    flavor: options[:flavor],
    sentry_dsn: options[:sentry_dsn],
    keystore_path: options[:keystore_path],
    keystore_password: options[:keystore_password],
    key_alias: options[:key_alias],
    key_password: options[:key_password]
  )
end

lane :android_build_irmagobridge do
  Dir.chdir("../yivi_core") do
    sh(
      "gomobile", "bind",
      "-target", "android",
      "-androidapi", "26",
      "-o", "android/irmagobridge/irmagobridge.aar",
      "github.com/privacybydesign/irmamobile/irmagobridge"
    )
  end
end

lane :android_build_apk do |options|
  android_build_app(
    build_type: "apk",
    flavor: options[:flavor],
    sentry_dsn: options[:sentry_dsn],
    keystore_path: options[:keystore_path],
    keystore_password: options[:keystore_password],
    key_alias: options[:key_alias],
    key_password: options[:key_password]
  )
  Dir.chdir("..") do
    sh("cp ./yivi_app/build/app/outputs/apk/#{options[:flavor]}/release/*.apk ./fastlane/build/")
  end
end

lane :android_build_appbundle do |options|
  android_build_app(
    build_type: "appbundle",
    flavor: options[:flavor],
    sentry_dsn: options[:sentry_dsn],
    keystore_path: options[:keystore_path],
    keystore_password: options[:keystore_password],
    key_alias: options[:key_alias],
    key_password: options[:key_password]
  )
  Dir.chdir("..") do
    sh("cp ./yivi_app/build/app/outputs/bundle/#{options[:flavor]}Release/*.aab ./fastlane/build/")
  end
end

private_lane :android_build_app do |options|
  update_schemes()
  commit = last_git_commit()
  write_sentrydata(
    dsn: options[:sentry_dsn],
    version: commit[:commit_hash]
  )

  # In Fastlane it's not possible to mask secrets in the command, while showing the shell output of the command.
  # Therefore, we use environment variables to hide parameter values.
  puts "In the following command, parameter values are hidden using environment variables."
  cmd = "flutter build $FASTLANE_BUILD_TYPE --flavor $FASTLANE_FLAVOR --release"
  ENV["FASTLANE_BUILD_TYPE"] = options[:build_type]
  ENV["FASTLANE_FLAVOR"] = options[:flavor]
  if options[:keystore_path]
    ENV["FASTLANE_KEYSTORE_PATH"] = File.absolute_path(options[:keystore_path])
    ENV["FASTLANE_KEYSTORE_PASSWORD"] = options[:keystore_password]
    ENV["FASTLANE_KEY_ALIAS"] = options[:key_alias]
    ENV["FASTLANE_KEY_PASSWORD"] = options[:key_password]
    cmd += " -PkeyStoreFile=$FASTLANE_KEYSTORE_PATH -PkeyStorePassword=$FASTLANE_KEYSTORE_PASSWORD"
    cmd += " -PkeyAlias=$FASTLANE_KEY_ALIAS -PkeyPassword=$FASTLANE_KEY_PASSWORD"
  end

  Dir.chdir("../yivi_app") do
    sh(cmd)
  end
end

lane :android_build_integration_test do
  update_schemes()
  Dir.chdir("../yivi_app") do
    sh(
      "flutter", "build", "apk",
      "--flavor", "alpha",
      "--debug",
      "./integration_test/test_all.dart"
    )
    Dir.chdir("./android") do
      sh("./gradlew", "app:assembleAndroidTest")
    end
    sh("cp ./build/app/outputs/apk/alpha/debug/*.apk ../fastlane/build/")
    sh("cp ./build/app/outputs/apk/androidTest/alpha/debug/*.apk ../fastlane/build/")
  end
end

lane :ios_build do |options|
  ios_build_irmagobridge()
  ios_build_app(
    flavor: options[:flavor],
    sentry_dsn: options[:sentry_dsn],
    certificate_path: options[:certificate_path],
    certificate_password: options[:certificate_password],
    provisioning_profile_path: options[:provisioning_profile_path],
    code_signing_identity: options[:code_signing_identity]
  )
end

lane :ios_build_irmagobridge do
  Dir.chdir("../yivi_core") do
    sh(
      "gomobile", "bind",
      "-target", "ios",
      "-iosversion", "15.6",
      "-o", "ios/Irmagobridge.xcframework",
      "github.com/privacybydesign/irmamobile/irmagobridge"
    )
  end
end

lane :ios_build_app do |options|
  display_name = ""
  app_identifier = ""
  export_method = ""

  # When a distribution certificate is set, we assume that alpha builds are meant for ad hoc distribution
  # and beta builds for app store distribution.
  case options[:flavor]
  when "alpha"
    display_name = "Yivi Alpha"
    app_identifier = "foundation.privacybydesign.irmamob.alpha"
    export_method = "ad-hoc"
  when "beta"
    display_name = "Yivi"
    app_identifier = "foundation.privacybydesign.irmamob"
    export_method = "app-store"
  else
    raise "Unsupported flavor"
  end
  if options[:code_signing_identity]&.include?("Developer")
    export_method = "development"
  end

  update_schemes()
  update_app_identifier(
    xcodeproj: "yivi_app/ios/Runner.xcodeproj",
    plist_path: "Runner/Info.plist",
    app_identifier: app_identifier
  )
  update_info_plist(
    xcodeproj: "yivi_app/ios/Runner.xcodeproj",
    plist_path: "Runner/Info.plist",
    display_name: display_name
  )
  commit = last_git_commit()
  write_sentrydata(
    dsn: options[:sentry_dsn],
    version: commit[:commit_hash]
  )

  set_provisioning_profile(
    provisioning_profile_path: options[:provisioning_profile_path],
    certificate_path: options[:certificate_path],
    certificate_password: options[:certificate_password],
    code_signing_identity: options[:code_signing_identity]
  )

  # Set export method in plist file.
  set_info_plist_value(
    path: "yivi_app/ios/Runner/Flutter-Build-IPA.plist",
    key: "method",
    value: export_method
  )

  Dir.chdir("../yivi_app") do
    sh("flutter", "build", "ipa", "--release", "--export-options-plist", "./ios/Runner/Flutter-Build-IPA.plist")
    sh("cp ./build/ios/ipa/*.ipa ../fastlane/build/app-#{options[:flavor]}-ios-#{export_method}.ipa")
  end
end

lane :ios_build_integration_test do |options|
  update_schemes()
  set_provisioning_profile(
    provisioning_profile_path: options[:provisioning_profile_path],
    certificate_path: options[:certificate_path],
    certificate_password: options[:certificate_password],
    code_signing_identity: options[:code_signing_identity]
  )

  # Navigate to the Flutter project root directory
  Dir.chdir("../yivi_app") do
    # Get a list of all Dart test files in the integration_test directory
    test_files = Dir["integration_test/**/*_test.dart"]

    test_files.each do |test_file|
      # Extract the base name of the test file to use for naming zip files
      test_name = File.basename(test_file, ".dart")

      # Run Flutter build for each test file
      sh(
        "flutter", "build", "ios",
        "--release",
        "--config-only",
        test_file
      )

      # Navigate to the ios directory and run xcodebuild for each test
      Dir.chdir("./ios") do
        sh(
          "xcodebuild",
          "-workspace", "Runner.xcworkspace",
          "-scheme", "Runner",
          "-config", "Flutter/Release.xcconfig",
          "-derivedDataPath", "../build/ios_integ",
          "-sdk", "iphoneos",
          "build-for-testing"
        )
      end

      # Create a zip for each test output
      Dir.chdir("./build/ios_integ/Build/Products") do
        # XCode does not clean left-over build artifacts created by earlier XCode versions.
        # Therefore, we ensure only the xctestrun file for the latest iOS SDK is included.
        # Get the latest .xctestrun file (assumes the latest file is the one just built)
        test_run_name = sh("ls -r1 *.xctestrun | head -n 1").strip

        # Remove any existing zip file with the same name
        sh("rm -f ../../../../../fastlane/build/#{test_name}_ios_tests.zip")

        # Zip the build artifacts for the current test file
        sh("zip -r ../../../../../fastlane/build/#{test_name}_ios_tests.zip Release-iphoneos #{test_run_name}")
      end
    end
  end
end

private_lane :update_schemes do
  Dir.chdir("../irma_configuration/pbdf") do
    sh("git", "checkout", "master")
    sh("git", "pull", "-f")
  end
  Dir.chdir("../irma_configuration/pbdf-requestors") do
    sh("git", "checkout", "master")
    sh("git", "pull", "-f")
  end
  Dir.chdir("../irma_configuration/irma-demo") do
    sh("git", "checkout", "master")
    sh("git", "pull", "-f")
    sh("rm", "-f", "sk.pem")
  end
  Dir.chdir("../irma_configuration/irma-demo-requestors") do
    sh("git", "checkout", "master")
    sh("git", "pull", "-f")
  end
end

private_lane :write_sentrydata do |options|
  erb(
    template: "fastlane/sentry_dsn.erb",
    destination: "yivi_core/lib/sentry_dsn.dart",
    placeholders: {
      :dsn => options[:dsn],
      :version => options[:version]
    }
  )
end

private_lane :set_provisioning_profile do |options|
  # When a certificate path is given, we ensure it is properly installed.
  if options[:certificate_path]
    keychain_path = File.absolute_path("profiles/fastlane.keychain")
    certificate_password = options[:certificate_password] || ""
    create_keychain(
      path: keychain_path,
      password: certificate_password,
      unlock: true,
      timeout: 600
    )
    import_certificate(
      certificate_path: File.absolute_path(options[:certificate_path]),
      certificate_password: certificate_password,
      keychain_path: keychain_path,
      keychain_password: certificate_password
    )
  end

  # When a provisioning profile is given, we ensure it is installed and selected in the project.
  # Otherwise, we use the profile that is currently selected in XCode.
  if options[:provisioning_profile_path]
    provisioning_profile_path = File.absolute_path(options[:provisioning_profile_path])

    # Get uuid of provisioning profile
    uuid_regex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/
    uuid = ""
    File.open(provisioning_profile_path, "r:BINARY") do |file|
      file.each_line do |line|
        encoded_line = line.encode("UTF-8", invalid: :replace, undef: :replace)
        match_data = encoded_line.match(uuid_regex)
        if match_data
          uuid = match_data[0]
          break
        end
      end
    end

    install_provisioning_profile(
      path: provisioning_profile_path
    )
    update_project_provisioning(
      xcodeproj: "yivi_app/ios/Runner.xcodeproj",
      profile: provisioning_profile_path,
      target_filter: "^Runner$",
      code_signing_identity: options[:code_signing_identity] || "iPhone Distribution"
    )
    # Set uuid in plist file for flutter
    set_info_plist_value(
      path: "yivi_app/ios/Runner/Flutter-Build-IPA.plist",
      key: "provisioningProfiles",
      subkey: "foundation.privacybydesign.irmamob",
      value: uuid
    )
    set_info_plist_value(
      path: "yivi_app/ios/Runner/Flutter-Build-IPA.plist",
      key: "provisioningProfiles",
      subkey: "foundation.privacybydesign.irmamob.alpha",
      value: uuid
    )
  end
end
