# iOS release signing and TestFlight upload, driven by .github/workflows/native-release.yml.
#
# Automatic signing cannot work on a CI runner with no registered devices: it signs
# the archive for development and then needs a development provisioning profile, which
# requires a device. So this signs manually for distribution instead. The distribution
# certificate and the App Store provisioning profile are created (or reused) through the
# App Store Connect API key, which means no certificate or profile has to be stored as a
# secret: the key the workflow already holds is enough.
#
# The lane expects these in the environment (the workflow sets them):
#   ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_PATH  — the App Store Connect API key
#   APPLE_TEAM_ID                            — the developer team
#   MARKETING_VERSION, CURRENT_PROJECT_VERSION — the version and build number
#   RUNNER_TEMP                              — a writable scratch directory

default_platform(:ios)

APP_IDENTIFIER = "com.liketrek.trek"
PROJECT = "ios/App/App.xcodeproj"

# Every signed target with its bundle identifier: the app and its extensions
# (ios/scripts/add-extension.rb put them in the project). Each gets its own App
# Store profile, created or reused through the API key like the app's.
SIGNED_TARGETS = {
  "App" => APP_IDENTIFIER,
  "NotificationService" => "com.liketrek.trek.notifications",
  "TREKWidgets" => "com.liketrek.trek.widgets",
}

platform :ios do
  desc "Build the signed Release app and upload it to TestFlight (internal testing)."
  lane :beta do
    key = app_store_connect_api_key(
      key_id: ENV.fetch("ASC_KEY_ID"),
      issuer_id: ENV.fetch("ASC_ISSUER_ID"),
      key_filepath: ENV.fetch("ASC_KEY_PATH"),
      in_house: false,
    )

    # A throwaway keychain with a known password, so fastlane can unlock the imported
    # private key for code signing (without the password it cannot set the key's access
    # list and the certificate is never usable).
    keychain_name = "trek_ci.keychain"
    keychain_password = "trek-ci-#{ENV.fetch('GITHUB_RUN_ID', 'local')}"
    create_keychain(
      name: keychain_name,
      password: keychain_password,
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: false,
    )
    keychain_path = File.expand_path("~/Library/Keychains/#{keychain_name}-db")

    # Import the one distribution certificate from its stored .p12, rather than
    # minting a new one each run — Apple caps distribution certificates at two, so
    # a fresh one per build would quickly hit the limit. The matching App Store
    # profile is created (or reused) through the API key.
    import_certificate(
      certificate_path: ENV.fetch("CERT_P12_PATH"),
      certificate_password: ENV.fetch("CERT_P12_PASSWORD"),
      keychain_name: keychain_name,
      keychain_path: keychain_path,
      keychain_password: keychain_password,
    )
    profiles = {}
    SIGNED_TARGETS.each do |target, identifier|
      get_provisioning_profile(
        api_key: key,
        app_identifier: identifier,
        development: false,
      )
      profiles[identifier] = lane_context[SharedValues::SIGH_NAME]

      # Pin each target to its profile for this build; automatic signing stays the
      # default a developer sees when they open the project in Xcode.
      update_code_signing_settings(
        path: PROJECT,
        use_automatic_signing: false,
        team_id: ENV.fetch("APPLE_TEAM_ID"),
        code_sign_identity: "Apple Distribution",
        bundle_identifier: identifier,
        profile_name: profiles[identifier],
        targets: [target],
      )
    end

    build_app(
      project: PROJECT,
      scheme: "App",
      configuration: "Release",
      export_method: "app-store",
      output_directory: File.join(ENV.fetch("RUNNER_TEMP"), "export"),
      xcargs: [
        "MARKETING_VERSION=#{ENV.fetch('MARKETING_VERSION')}",
        "CURRENT_PROJECT_VERSION=#{ENV.fetch('CURRENT_PROJECT_VERSION')}",
      ].join(" "),
      export_options: {
        signingStyle: "manual",
        provisioningProfiles: profiles,
      },
    )

    # Upload to App Store Connect; it shows up in TestFlight after Apple finishes processing.
    # Internal testing needs no App Review, so nothing here submits the app for review.
    # "What to Test" comes from the release tag's message (native-release.yml). Apple only
    # takes it once the build is processed, so with notes the lane waits for that.
    notes = ENV.fetch("TESTFLIGHT_NOTES", "").strip
    upload_to_testflight(
      api_key: key,
      changelog: notes.empty? ? nil : notes,
      skip_waiting_for_build_processing: notes.empty?,
      skip_submission: true,
    )
  end
end
