# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Note: `update_fastlane` is intentionally NOT called here.
# Fastlane versions are pinned in the repo's Gemfile/Gemfile.lock; allowing the
# tool to self-update mid-run on CI has historically caused flaky failures.
# Run `bundle update fastlane` locally when you need a new version.

default_platform(:android)

# Absolute path to fastlane/metadata/android, anchored to this Fastfile's own
# directory (__dir__ is the fastlane/ folder, evaluated lexically regardless of
# the process working directory). Using an absolute path avoids a CWD-resolution
# mismatch: fastlane runs plain-Ruby blocks (FileUtils/File.write) and the
# supply / amazon actions with different effective working directories, so a
# relative "metadata/android" can be created in one place and looked up in
# another — which surfaced as "Could not find folder metadata/android".
def android_metadata_path
  File.expand_path("metadata/android", __dir__)
end

# Absolute path to the checked-in "what's new" changelog file for the given
# versionCode. supply (and the Amazon plugin) read release notes from
# <metadata_path>/en-US/changelogs/<versionCode>.txt. Returns nil when
# version_code <= 0 (0 is the deploy.yml sentinel meaning "don't trust the
# manifest versionCode").
def changelog_file_path(version_code)
  vc = version_code.to_i
  return nil if vc <= 0
  File.join(android_metadata_path, "en-US", "changelogs", "#{vc}.txt")
end

# True when a non-empty checked-in changelog file exists for the versionCode.
def changelog_file_exists?(version_code)
  path = changelog_file_path(version_code)
  !path.nil? && File.exist?(path) && !File.read(path).strip.empty?
end

# Write the "what's new" changelog file for the given versionCode and return
# true when a file was actually written. Release notes are normally checked into
# the repo at <metadata_path>/en-US/changelogs/<versionCode>.txt; this helper is
# only used to (over)write that file from an inline string when one is passed
# explicitly (legacy override). Writes nothing and returns false when the notes
# are blank or version_code <= 0.
def write_changelog_file(version_code, release_notes)
  return false if release_notes.nil? || release_notes.to_s.strip.empty?
  changelog_path = changelog_file_path(version_code)
  return false if changelog_path.nil?

  FileUtils.mkdir_p(File.dirname(changelog_path))
  File.write(changelog_path, release_notes)
  UI.message("Wrote release notes to #{changelog_path} for version code #{version_code.to_i}")
  true
end

# Prepend a pre-release notice to the checked-in changelog file for the given
# versionCode so internal testers can see it's a release candidate. Modifies the
# ephemeral CI checkout only (never committed). Returns true when the notice was
# prepended. Does nothing when the notice is blank, the versionCode is invalid,
# or no changelog file exists.
def prepend_prerelease_notice(version_code, notice)
  return false if notice.nil? || notice.to_s.strip.empty?
  path = changelog_file_path(version_code)
  return false if path.nil? || !File.exist?(path)

  existing = File.read(path)
  File.write(path, "#{notice.to_s.strip}\n\n#{existing}")
  UI.message("Prepended pre-release notice to #{path} for version code #{version_code.to_i}")
  true
end

# Prepare the "what's new" changelog for upload. Precedence:
#   1. If options[:release_notes] is provided, (over)write the checked-in
#      changelog file from it (legacy inline override).
#   2. Otherwise the checked-in changelog file for the versionCode is used as-is.
#   3. If options[:prerelease_notice] is provided, prepend it to the changelog
#      (pre-release / internal-track builds only).
# Returns true when a changelog file exists for the versionCode (so supply
# should upload it), false otherwise.
def prepare_changelog(options)
  write_changelog_file(options[:version_code], options[:release_notes])
  prepend_prerelease_notice(options[:version_code], options[:prerelease_notice])
  changelog_file_exists?(options[:version_code])
end

platform :android do
  desc "Runs all the tests"
  lane :test_and_lint do
    gradle(task: "testAndroidDebugUnitTest")
    gradle(task: "lintAndroidDebug")
  end

  desc "Build debug and test APK and generate screenshots"
  lane :build_and_screengrab do
    build_android_app(
      task: 'assemble',
      build_type: 'Debug'
    )
    build_android_app(
      task: 'assemble',
      build_type: 'AndroidTest'
    )
    sh("cp ../app/build/outputs/apk/android/debug/app-android-debug-v*.apk ../app/build/outputs/apk/android/debug/app-android-debug.apk")
    sh("./avd-screenshot-setup.sh on")
    # Dismiss any stale system dialogs once before starting screengrab.
    # ANR dialogs are already suppressed via `hide_error_dialogs 1` set in
    # avd-screenshot-boot.sh and avd-screenshot-setup.sh — a continuous
    # watchdog loop is not needed and floods the emulator with broadcasts,
    # which can stall adb and prevent screengrab from running.
    sh("adb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS 2>/dev/null || true")
    screengrab()
    sh("./avd-screenshot-setup.sh off")
    sh("cp metadata/android/en-US/images/phoneScreenshots/*.png ../images/screenshots/")
  end

  desc "Deploy the app to Google Play Store (tester track) using a pre-built APK."
  lane :deploy_playstore_test do |options|
    begin
      UI.message("Deploying to Play tester track with options: #{options.inspect}")
      UI.user_error!("APK path must be provided via options[:apk_path] and exist.") unless options[:apk_path] && File.exist?(options[:apk_path])

      # Allow the caller to pick which Play tester track to deploy to.
      # Defaults to 'internal' (Internal testing). Other valid values: 'alpha'
      # (Closed testing), 'beta' (Open testing).
      track = options[:track] && !options[:track].to_s.strip.empty? ? options[:track].to_s : 'internal'
      UI.user_error!("Invalid track: #{track}") unless %w[internal alpha beta].include?(track)

      supply_params = {
        track: track,
        apk: options[:apk_path],
        # json_key_file is already set in Appfile.
        # Upload only the APK (and the changelog handled below) — never the
        # store listing text, images, or screenshots — so a build/track deploy
        # can't overwrite the live Play listing.
        # android_metadata_path is absolute (see helper) to avoid CWD ambiguity.
        metadata_path: android_metadata_path,
        skip_upload_metadata: true,
        skip_upload_images: true,
        skip_upload_screenshots: true,
        skip_upload_changelogs: true
      }

      # supply has no inline "changelogs" option; the "what's new" text is read
      # from the checked-in versionCode-named file under the metadata path.
      # Enable changelog upload only when a changelog file exists for this
      # versionCode. For pre-release/internal builds, options[:prerelease_notice]
      # is prepended to the checked-in notes (ephemeral CI checkout only).
      supply_params[:skip_upload_changelogs] = false if prepare_changelog(options)

      supply(supply_params)
      UI.success("Successfully deployed to Google Play Store (#{track} track)!")
    rescue => ex
      UI.error("Error deploying to Google Play Store (tester track): #{ex}")
      raise ex
    end
  end

  desc "Release to the Google Play Store Production track.\n" \
       "Promotes an already-uploaded release by versionCode (default), or uploads " \
       "a pre-built APK when options[:apk_path] is provided."
  lane :deploy_playstore_production do |options|
    begin
      UI.message("Deploying to Production Track with options: #{options.inspect}")

      # Two modes:
      #   * Promote (default): the APK was already uploaded (and published) to a
      #     tester track by the pre-release workflow. Google Play rejects
      #     re-uploading an already-published APK ("Cannot update a published
      #     APK"), so promote the existing release to production by versionCode.
      #   * Upload: when a caller passes options[:apk_path] for a versionCode that
      #     was never uploaded to any track, upload the binary straight to
      #     production.
      upload_mode = options[:apk_path] && !options[:apk_path].to_s.strip.empty?

      supply_params = {
        # Upload only the APK/changelog — never the store listing text, images,
        # or screenshots.
        # android_metadata_path is absolute (see helper) to avoid CWD ambiguity.
        metadata_path: android_metadata_path,
        skip_upload_metadata: true,
        skip_upload_images: true,
        skip_upload_screenshots: true,
        skip_upload_changelogs: true
      }

      if upload_mode
        UI.user_error!("apk_path was provided but does not exist: #{options[:apk_path]}") unless File.exist?(options[:apk_path])
        supply_params[:track] = 'production'
        supply_params[:apk] = options[:apk_path]
      else
        # Promotion is keyed off the versionCode, so it is required in this mode.
        version_code = options[:version_code].to_i
        UI.user_error!("Provide options[:apk_path] to upload, or a positive version_code to promote to production.") unless version_code > 0

        # Track the pre-release build was uploaded to (see deploy_playstore_test /
        # release.yml, which uses 'internal'). Overridable for flexibility.
        from_track = options[:from_track] && !options[:from_track].to_s.strip.empty? ? options[:from_track].to_s : 'internal'
        UI.user_error!("Invalid from_track: #{from_track}") unless %w[internal alpha beta].include?(from_track)

        supply_params[:track] = from_track
        supply_params[:track_promote_to] = 'production'
        supply_params[:version_code] = version_code
        # Promote the existing artifact — do not (re-)upload any binary.
        supply_params[:skip_upload_apk] = true
        supply_params[:skip_upload_aab] = true
      end

      # supply has no inline "changelogs" option; the "what's new" text is read
      # from the checked-in versionCode-named file under the metadata path.
      # Enable changelog upload only when a changelog file exists for this
      # versionCode.
      supply_params[:skip_upload_changelogs] = false if prepare_changelog(options)

      supply(supply_params)
      UI.success("Successfully released to Google Play Store (Production Track)!")
    rescue => ex
      UI.error("Error deploying to Google Play Store (Production): #{ex}")
      raise ex
    end
  end

  desc "Deploy the app to Amazon Appstore using a pre-built APK."
  lane :deploy_amazon_appstore do |options|
    begin
      UI.message("Deploying to Amazon Appstore with options: #{options.inspect}")
      UI.user_error!("APK path must be provided via options[:apk_path] and exist.") unless options[:apk_path] && File.exist?(options[:apk_path])

      # Gradle build steps are removed. APK is provided via options[:apk_path].

      # The "what's new" text is read from the checked-in versionCode-named file
      # under the metadata path. The deploy workflow passes version_code="0" as a
      # sentinel when the operator supplied an explicit release_tag (in which case
      # the workflow can't trust main's manifest); prepare_changelog skips the
      # file lookup in that case and the plugin relies on existing files (if any).
      unless prepare_changelog(options)
        UI.important("No checked-in changelog file for this version code; relying on existing changelog files (if any).")
      end

      # Check to ensure ENV variables are set
      UI.user_error!("AMAZON_CLIENT_ID and AMAZON_CLIENT_SECRET must be set in environment variables.") unless ENV['AMAZON_CLIENT_ID'] && ENV['AMAZON_CLIENT_SECRET']

      # The Amazon Appstore listing is registered under the amazon-flavor
      # applicationId — com.spencerpages + the ".amazon" applicationIdSuffix from
      # app/build.gradle — which differs from the Play package (com.spencerpages)
      # that the Appfile sets. The plugin sends package_name as the App
      # Submission API's applicationId (applications/<package_name>/edits), so
      # without this override it inherits the Appfile's com.spencerpages and
      # Amazon rejects it with "No app found with the entered Inputs".
      amazon_package_name = options[:package_name] && !options[:package_name].to_s.strip.empty? ? options[:package_name].to_s : 'com.spencerpages.amazon'

      amazon_params = {
        apk: options[:apk_path], # Use passed APK path
        package_name: amazon_package_name, # Amazon app id (see note above); NOT the Play package
        client_id: ENV['AMAZON_CLIENT_ID'],
        client_secret: ENV['AMAZON_CLIENT_SECRET'],
        metadata_path: android_metadata_path, # Absolute (see helper) to avoid CWD ambiguity
        skip_upload_changelogs: false # Ensure plugin tries to read changelog files
        # version_name: options[:version_name] # Optional: if plugin uses it for metadata
      }

      upload_to_amazon_appstore(amazon_params)
      UI.success("Successfully deployed to Amazon Appstore!")
    rescue => ex
      UI.error("Error deploying to Amazon Appstore: #{ex}")
      raise ex # Re-raise the exception to fail the lane
    end
  end
end