# Customise this file, documentation can be found here:
# https://github.com/KrauseFx/fastlane/tree/master/docs
fastlane_require 'fileutils'
fastlane_require 'shellwords'

fastlane_version "2.141.0"
default_platform :ios

STAGING_API_URL = 'https://staging.exp.host/'

# path relative to Fastfile
output_directory = File.expand_path('./Deployment')

def update_bundle_versions
  bundle_short_version = Actions.lane_context[SharedValues::VERSION_NUMBER]
  bundle_version = bundle_short_version + ".10" + number_of_commits.to_s

  puts "Setting CFBundleVersion for Exponent to #{bundle_version}..."
  set_info_plist_value(
    path: "./ios/Exponent/Supporting/Info.plist",
    key: "CFBundleVersion",
    value: bundle_version
  )

  puts "Setting CFBundleVersion to #{bundle_version} and CFBundleShortVersionString #{bundle_short_version} for ExpoNotificationServiceExtension..."
  set_info_plist_value(
    path: "./ios/ExpoNotificationServiceExtension/Info.plist",
    key: "CFBundleVersion",
    value: bundle_version
  )

  set_info_plist_value(
    path: "./ios/ExpoNotificationServiceExtension/Info.plist",
    key: "CFBundleShortVersionString",
    value: bundle_short_version
  )
end

# This will keep a copy of the original Info.plist
@original_info_plist = nil
@original_notification_extension_info_plist = nil

def save_original_info_plists
  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "Exponent/Supporting/Info.plist",
    block: proc do |plist|
      @original_info_plist = Marshal.load(Marshal.dump(plist))
    end
  )

  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "ExpoNotificationServiceExtension/Info.plist",
    block: proc do |plist|
      @original_notification_extension_info_plist = Marshal.load(Marshal.dump(plist))
    end
  )
end

def restore_original_info_plists
  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "Exponent/Supporting/Info.plist",
    block: proc do |plist|
      puts "Restoring original Exponent/Info.plist..."
      plist.replace(@original_info_plist)
    end
  )

  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "ExpoNotificationServiceExtension/Info.plist",
    block: proc do |plist|
      puts "Restoring original ExpoNotificationServiceExtension/Info.plist..."
      plist.replace(@original_notification_extension_info_plist)
    end
  )
end

def remove_background_location
  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "Exponent/Supporting/Info.plist",
    block: proc do |plist|
      puts "Removing background location permission strings and background mode..."

      puts "Deleting NSLocationAlwaysAndWhenInUseUsageDescription key..."
      plist.delete("NSLocationAlwaysAndWhenInUseUsageDescription")

      puts "Deleting NSLocationAlwaysUsageDescription key..."
      plist.delete("NSLocationAlwaysUsageDescription")

      puts "Deleting location from UIBackgroundModes..."
      plist["UIBackgroundModes"].delete("location")
    end
  )
end

def remove_background_audio
  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "Exponent/Supporting/Info.plist",
    block: proc do |plist|
      puts "Removing background audio background mode..."
      plist["UIBackgroundModes"].delete("audio")
    end
  )
end

def remove_background_notification
  update_info_plist(
    xcodeproj: "ios/Exponent.xcodeproj",
    plist_path: "Exponent/Supporting/Info.plist",
    block: proc do |plist|
      puts "Removing remote-notification from UIBackgroundModes..."
      plist["UIBackgroundModes"].delete("remote-notification")
    end
  )
end

def configure_for_app_store
  puts "Configuring app for App Store submission..."
  remove_background_location
  remove_background_audio
  remove_background_notification
end

platform :ios do
  before_all do
    FileUtils.mkdir_p(output_directory)
    Actions.lane_context[SharedValues::VERSION_NUMBER] = get_version_number(
      xcodeproj: "./ios/Exponent.xcodeproj",
      target: "Expo Go (versioned)"
    )
    save_original_info_plists
    update_bundle_versions
    setup_circle_ci
  end

  lane :sync_screenshots do
    upload_to_app_store(
      team_id: "17102800",
      app_identifier: "host.exp.Exponent",
      screenshots_path: "./fastlane/screenshots",
      skip_binary_upload: true,
      skip_metadata: true
    )
  end

  lane :prepare_schemes do |options|
    recreate_schemes(
      project: "ios/Pods/#{options[:pod]}.xcodeproj"
    )
  end

  lane :test_module do |options|
    scan(
      project: "ios/Pods/#{options[:pod]}.xcodeproj",
      # This scheme is autogenerated by CocoaPods when a testspec is included in the Podfile
      scheme: "#{options[:pod]}-Unit-#{options[:testSpecName]}",
      clean: false,
    )
  end

  lane :unit_tests do |options|
    workspace = "apps/native-tests/ios/NativeTests.xcworkspace"

    generated_scheme = generate_test_scheme(
      scheme_name: 'NativeTests',
      workspace_path: workspace,
      targets: options[:targets]
    )

    run_tests(
      workspace: workspace,
      scheme: generated_scheme,
      devices: ["iPhone 14 Pro"],
      configuration: 'Debug',
      clean: false,
      skip_build: true,
      prelaunch_simulator: true,
      skip_detect_devices: true,
      skip_package_dependencies_resolution: true,
      derived_data_path: "/tmp/ExpoUnitTestsDerivedData",
      xcargs: 'CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO',
    )
  end

  lane :test do
    scan(
      workspace: "ios/Exponent.xcworkspace",
      scheme: "Exponent",
      devices: ["iPhone SE", "iPhone X"],
      clean: false,
    )
  end

  lane :create_simulator_build do |options|
    directory = "./ios/simulator-build"
    puts "Build will be written to: " + File.expand_path(directory)

    flavor = options.fetch(:flavor, "versioned")

    xcbuild(
      workspace: "ios/Exponent.xcworkspace",
      scheme: "Expo Go (#{flavor})",
      sdk: "iphonesimulator",
      configuration: "Release",
      derivedDataPath: directory,
      xcargs: "ARCHS=\"x86_64\" ONLY_ACTIVE_ARCH=NO -quiet",
      raw_buildlog: is_ci?
    )
  end

  lane :create_expo_client_build do |options|
    directory = "./ios" + prompt(
      ci_input: "/../expo-client-build",
      text: "Please enter the path where we should output the build, relative to ./ios (i.e. ../my-build):",
    )
    puts "Build will be written to: " + File.expand_path(directory)

    flavor = options.fetch(:flavor, "versioned")

    xcbuild(
      workspace: "ios/Exponent.xcworkspace",
      scheme: "Expo Go (#{flavor})",
      configuration: "Release",
      derivedDataPath: directory,
      sdk: "iphoneos",
      destination: "generic/platform=iOS",
      archive: true,
      archive_path: directory + "/Exponent.xcarchive",
      xcargs: "CODE_SIGNING_ALLOWED=NO APP_OWNER=Public -quiet"
    )
  end

  lane :release do |options|
    cert(
      team_id: "C8D8QTF339",
    )

    sigh(
      app_identifier: "host.exp.Exponent",
      output_path: output_directory,
      filename: "Exponent-distribution.mobileprovision",
    )

    xcodebuild_args = {
      APP_PROVISIONING_PROFILE: Actions.lane_context[Actions::SharedValues::SIGH_UDID],
    }
    xcodebuild_args = xcodebuild_args.map do |k,v|
      "#{k.to_s.shellescape}=#{v.shellescape}"
    end.join ' '

    configure_for_app_store

    flavor = options.fetch(:flavor, "versioned")

    gym(
      workspace: "ios/Exponent.xcworkspace",
      scheme: "Expo Go (#{flavor})",
      configuration: "Release",
      xcargs: xcodebuild_args,
      clean: true,
      output_directory: output_directory,
      output_name: "Exponent.ipa",
    )

    deliver(
      team_id: "17102800",
    )
  end

  after_all do |lane|
    # FileUtils.rm_rf(output_directory)
    restore_original_info_plists
  end

  error do |lane, exception|
    # slack(
    #   message: exception.message,
    #   success: false
    # )
  end

end

platform :android do

  private_lane :verify_changelog_exists do |version_code: |
    changelog_path = "android/metadata/en-US/changelogs/#{version_code}.txt"
    UI.user_error!("Missing changelog file at #{changelog_path}") unless File.exist?(changelog_path)
    UI.message("Changelog exists for version code #{version_code}")
  end

  private_lane :verify_upload_to_staging do |version_name: |
    UI.message "Skipping staging verification step"
    # require 'open-uri'
    # staging_version = nil
    # begin
    #   versions = JSON.parse(open(URI.join(STAGING_API_URL, "/--/api/v2/versions")).read)
    #   latest_major_version = versions['sdkVersions'].keys.map(&:to_i).sort().reverse().first()
    #   latest_version = versions['sdkVersions']["#{latest_major_version}.0.0"]
    #   staging_version = latest_version['androidClientVersion']
    # rescue StandardError => e
    #   UI.user_error!("Unable to validate androidClientVersion for latest SDK version on versions endpoint: #{e.inspect}. If this issue persists, remove the verify_upload_to_staging step from Fastfile and manually verify that the Android client has been deployed and tested on staging.")
    # end

    # unless staging_version == version_name
    #   UI.user_error!("APK version #{version_name} is not yet uploaded to staging. Please download from the client_android CI job, test and upload manually.")
    # end
    # UI.message "APK version #{version_name} has been uploaded to staging"
  end

  lane :start do |flavor: "unversioned"|
    gradle(
      project_dir: "android",
      task: "install",
      build_type: "Debug",
      flavor: flavor,
    )
    adb(command: "shell am start -n host.exp.exponent/host.exp.exponent.LauncherActivity")
  end

  lane :build do |build_type: "Debug", flavor: "versioned", sign: true, aab: false|
    ENV['ANDROID_UNSIGNED'] = '1' unless sign
    gradle(
      project_dir: "android",
      task: aab ? "app:bundle": "app:assemble",
      flavor: flavor,
      build_type: build_type,
    )
  end

  lane :upload_crashlytics_symbols do |flavor: "versioned"|
    gradle(
      project_dir: "android",
      task: "app:uploadCrashlyticsSymbolFile",
      flavor: flavor,
      build_type: "Release",
    )
  end

  lane :prod_release do
    build_gradle = File.read("../android/app/build.gradle")

    verify_changelog_exists(version_code: build_gradle.match(/versionCode (\d+)/)[1])
    verify_upload_to_staging(version_name: build_gradle.match(/versionName '([\d\.]+)'/)[1])

    supply(
      package_name: "host.exp.exponent",
      metadata_path: "./fastlane/android/metadata",
      aab: "./android/app/build/outputs/bundle/versionedRelease/app-versioned-release.aab",
      track: "production",
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end

end
