# Define app identifiers once for reuse across lanes
def app_identifiers
  [
    "io.bluewallet.bluewallet",
    "io.bluewallet.bluewallet.watch",
    "io.bluewallet.bluewallet.watch.extension",
    "io.bluewallet.bluewallet.Stickers",
    "io.bluewallet.bluewallet.MarketWidget"
  ]
end

def app_store_state_readable(state)
  states = {
    "DEVELOPER_REJECTED" => "Developer Rejected",
    "PREPARE_FOR_SUBMISSION" => "Prepare for Submission",
    "WAITING_FOR_REVIEW" => "Waiting for Review",
    "IN_REVIEW" => "In Review",
    "PENDING_DEVELOPER_RELEASE" => "Pending Developer Release",
    "READY_FOR_SALE" => "Ready for Sale",
    "REJECTED" => "Rejected",
    "METADATA_REJECTED" => "Metadata Rejected"
  }
  
  states[state] || state
end

default_platform(:android)
project_root = File.expand_path("..", __dir__)

# Add session caching for App Store Connect
def cached_app_store_connect_login
  # Skip if already authenticated and token is not expired
  if defined?(Spaceship::ConnectAPI) && Spaceship::ConnectAPI.token && !Spaceship::ConnectAPI.token.expired?
    UI.message("Using existing App Store Connect session")
    return true
  end
  
  UI.message("Logging in to App Store Connect...")
  
  # Try API key authentication first
  api_key_path = ENV["APPLE_API_KEY_PATH"] || "./appstore_api_key.json"
  
  if File.exist?(api_key_path) && ENV["APPLE_API_KEY_ID"] && ENV["APPLE_API_ISSUER_ID"]
    UI.message("Using API key authentication for App Store Connect")
    api_key = app_store_connect_api_key(
      key_id: ENV["APPLE_API_KEY_ID"],
      issuer_id: ENV["APPLE_API_ISSUER_ID"],
      key_filepath: api_key_path,
      duration: 1200, # 20 minute session
      in_house: false
    )
    
    # Store the API key in lane context for reuse
    ENV["SPACESHIP_CONNECT_API_KEY"] = api_key
    
    # Force App Store Connect API to use this key
    Spaceship::ConnectAPI.token = api_key
    
    UI.success("Successfully authenticated with App Store Connect API Key")
    return true
  elsif ENV["FASTLANE_USER"] && ENV["FASTLANE_PASSWORD"]
    UI.message("Using username/password from environment variables")
    # Use credentials from environment variables
    Spaceship::ConnectAPI.login(
      use_portal: true, 
      use_tunes: true,
      portal_team_id: ENV["TEAM_ID"],
      tunes_team_id: ENV["ITC_TEAM_ID"]
    )
    UI.success("Successfully authenticated with Apple ID")
    return true
  else
    UI.message("Using interactive username/password authentication")
    # Last resort - interactive login
    Spaceship::ConnectAPI.login(
      use_portal: true, 
      use_tunes: true
    )
    UI.success("Successfully authenticated with Apple ID")
    return true
  end
end

before_all do |lane, options|
  skip_auth_lanes = ['register_devices_from_txt']
  
  # Check if we need App Store Connect for this lane
  unless skip_auth_lanes.include?(lane)
    begin
      # Try to authenticate once at the beginning
      require 'spaceship'
      cached_app_store_connect_login
    rescue => ex
      UI.error("Authentication failed: #{ex.message}")
      # Continue anyway as some lanes might not need authentication
    end
  end
end

# ===========================
#       Android Lanes
# ===========================

platform :android do

  desc "Prepare the keystore file"
  lane :prepare_keystore do
    Dir.chdir(project_root) do
      keystore_file_hex = ENV['KEYSTORE_FILE_HEX']
      UI.user_error!("KEYSTORE_FILE_HEX environment variable is missing") if keystore_file_hex.nil?

      UI.message("Creating keystore from HEX...")
      File.write("bluewallet-release-key.keystore.hex", keystore_file_hex)

      sh("xxd -plain -revert bluewallet-release-key.keystore.hex > bluewallet-release-key.keystore") do |status|
        UI.user_error!("Error reverting hex to keystore") unless status.success?
      end
      UI.message("Keystore created successfully.")

      File.delete("bluewallet-release-key.keystore.hex")
    end
  end

  desc "Update version, build number, and sign APK"
  lane :update_version_build_and_sign_apk do
    Dir.chdir(project_root) do
      build_number = ENV['BUILD_NUMBER']
      UI.user_error!("BUILD_NUMBER environment variable is missing") if build_number.nil?
 
      # Extract versionName from build.gradle
      version_name = sh("grep versionName android/app/build.gradle | awk '{print $2}' | tr -d '\"'").strip
      UI.user_error!("Failed to extract versionName from build.gradle") if version_name.nil? || version_name.empty?
 
      # Update versionCode in build.gradle
      UI.message("Updating versionCode in build.gradle to #{build_number}...")
      build_gradle_path = "android/app/build.gradle"
      build_gradle_contents = File.read(build_gradle_path)
      new_build_gradle_contents = build_gradle_contents.gsub(/versionCode\s+\d+/, "versionCode #{build_number}")
      File.write(build_gradle_path, new_build_gradle_contents)
 
      # Determine branch name and sanitize it
      branch_name = ENV['GITHUB_HEAD_REF'] || `git rev-parse --abbrev-ref HEAD`.strip
      branch_name = branch_name.gsub(/[^a-zA-Z0-9_-]/, '_') # Replace non-alphanumeric characters with underscore
      branch_name = 'master' if branch_name.nil? || branch_name.empty?
 
      # Define APK name based on branch
      signed_apk_name = branch_name != 'master' ? 
        "BlueWallet-#{version_name}-#{build_number}-#{branch_name}.apk" : 
        "BlueWallet-#{version_name}-#{build_number}.apk"
 
      # Define paths
      unsigned_apk_path = "android/app/build/outputs/apk/release/app-release-unsigned.apk"
      signed_apk_path = "android/app/build/outputs/apk/release/#{signed_apk_name}"
 
      # Build APK
      UI.message("Building APK...")
      sh("cd android && ./gradlew assembleRelease --no-daemon")
      UI.message("APK build completed.")
 
      # Rename APK
      if File.exist?(unsigned_apk_path)
        UI.message("Renaming APK to #{signed_apk_name}...")
        FileUtils.mv(unsigned_apk_path, signed_apk_path)
        ENV['APK_OUTPUT_PATH'] = File.expand_path(signed_apk_path)
      else
        UI.error("Unsigned APK not found at path: #{unsigned_apk_path}")
        next
      end
 
      # Sign APK
      UI.message("Signing APK with apksigner...")
      apksigner_path = Dir.glob("#{ENV['ANDROID_HOME']}/build-tools/*/apksigner").sort.last
      UI.user_error!("apksigner not found in Android build-tools") if apksigner_path.nil? || apksigner_path.empty?
      sh("#{apksigner_path} sign --ks #{project_root}/bluewallet-release-key.keystore --ks-pass=pass:#{ENV['KEYSTORE_PASSWORD']} #{signed_apk_path}")
      UI.message("APK signed successfully: #{signed_apk_path}")
    end
  end
end

  desc "Upload APK to BrowserStack and post result as PR comment"
  lane :upload_to_browserstack_and_comment do
    Dir.chdir(project_root) do
      # Determine APK path
      apk_path = ENV['APK_PATH']
      if apk_path.nil? || apk_path.empty?
        UI.message("No APK path provided, searching for APK...")
        apk_path = `find ./ -name "*.apk"`.strip
        UI.user_error!("No APK file found") if apk_path.nil? || apk_path.empty?
      end

      # Upload to BrowserStack
      UI.message("Uploading APK to BrowserStack: #{apk_path}...")
      upload_to_browserstack_app_live(
        file_path: apk_path,
        browserstack_username: ENV['BROWSERSTACK_USERNAME'],
        browserstack_access_key: ENV['BROWSERSTACK_ACCESS_KEY']
      )

      # Extract BrowserStack URL
      app_url = ENV['BROWSERSTACK_LIVE_APP_ID']
      UI.user_error!("BrowserStack upload failed, no app URL returned") if app_url.nil? || app_url.empty?

      # Prepare PR comment
      apk_filename = File.basename(apk_path)
      apk_download_url = ENV['APK_OUTPUT_PATH'] # Ensure this path is accessible
      browserstack_hashed_id = app_url.gsub('bs://', '')
      pr_number = ENV['GITHUB_PR_NUMBER']

      comment_identifier = '### APK Successfully Uploaded to BrowserStack'

      comment = <<~COMMENT
        #{comment_identifier}

        You can test it on the following devices:
        
        - [Google Pixel 9 (Android 15)](https://app-live.browserstack.com/dashboard#os=android&os_version=15.0&device=Google+Pixel+8&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Google Pixel 8 (Android 14)](https://app-live.browserstack.com/dashboard#os=android&os_version=14.0&device=Google+Pixel+8&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Google Pixel 7 (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Google+Pixel+7&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Google Pixel 5 (Android 12)](https://app-live.browserstack.com/dashboard#os=android&os_version=12.0&device=Google+Pixel+5&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Google Pixel 3a (Android 9)](https://app-live.browserstack.com/dashboard#os=android&os_version=9.0&device=Google+Pixel+3a&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        
        - [Samsung Galaxy Z Fold 6 (Android 14)](https://app-live.browserstack.com/dashboard#os=android&os_version=14.0&device=Samsung+Galaxy+Z+Fold+6&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Samsung Galaxy Z Fold 5 (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Samsung+Galaxy+Z+Fold+5&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Samsung Galaxy Tab S9 (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=Samsung+Galaxy+Tab+S9&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        - [Samsung Galaxy Note 9 (Android 8.1)](https://app-live.browserstack.com/dashboard#os=android&os_version=8.1&device=Samsung+Galaxy+Note+9&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)

        - [OnePlus 11R (Android 13)](https://app-live.browserstack.com/dashboard#os=android&os_version=13.0&device=OnePlus+11R&app_hashed_id=#{browserstack_hashed_id}&scale_to_fit=true&speed=1&start=true&browser=chrome)
        **Filename**: [#{apk_filename}](#{apk_download_url})
        **BrowserStack App URL**: #{app_url}
      COMMENT

      # Delete Previous BrowserStack Comments
      if pr_number
        begin
          repo = ENV['GITHUB_REPOSITORY'] # Format: "owner/repo"
          repo_owner, repo_name = repo.split('/')

          UI.message("Fetching existing comments for PR ##{pr_number}...")

          comments_json = `gh api -X GET /repos/#{repo_owner}/#{repo_name}/issues/#{pr_number}/comments`
          comments = JSON.parse(comments_json)

          comments.each do |comment|
            if comment['body'].start_with?(comment_identifier)
              comment_id = comment['id']
              UI.message("Deleting previous comment ID: #{comment_id}...")
              `gh api -X DELETE /repos/#{repo_owner}/#{repo_name}/issues/comments/#{comment_id}`
              UI.success("Deleted comment ID: #{comment_id}")
            end
          end

        rescue => e
          UI.error("Failed to delete previous comments: #{e.message}")
        end
      else
        UI.important("No PR number found. Skipping deletion of previous comments.")
      end

      # Post New Comment to PR
      if pr_number
        begin
          escaped_comment = comment.gsub("'", "'\\''")
          sh("GH_TOKEN=#{ENV['GH_TOKEN']} gh pr comment #{pr_number} --body '#{escaped_comment}'")
          UI.success("Posted new comment to PR ##{pr_number}")
        rescue => e
          UI.error("Failed to post comment to PR: #{e.message}")
        end
      else
        UI.important("No PR number found. Skipping PR comment.")
      end
    end
end


# ===========================
#       iOS Lanes
# ===========================

platform :ios do
  # Add helper methods for error handling and retries
  def ensure_env_vars(vars)
    vars.each do |var|
      UI.user_error!("#{var} environment variable is missing") if ENV[var].nil? || ENV[var].empty?
    end
  end
  
  def log_success(message)
    UI.success("✅ #{message}")
  end
  
  def log_error(message)
    UI.error("❌ #{message}")
  end
  
  # Method to safely call actions with retry logic
  def with_retry(max_attempts = 3, action_name = "")
    attempts = 0
    begin
      attempts += 1
      yield
    rescue => e
      if attempts < max_attempts
        wait_time = 10 * attempts
        log_error("Attempt #{attempts}/#{max_attempts} for #{action_name} failed: #{e.message}")
        UI.message("Retrying in #{wait_time} seconds...")
        sleep(wait_time)
        retry
      else
        log_error("#{action_name} failed after #{max_attempts} attempts: #{e.message}")
        raise e
      end
    end
  end

  desc "Register new devices from a file"
  lane :register_devices_from_txt do
    UI.message("Registering new devices from file...")

    csv_path = "../../devices.txt" # Update this with the actual path to your file

    # Register devices using the devices_file parameter
    register_devices(
      devices_file: csv_path
    )

    UI.message("Devices registered successfully.")

    # Update provisioning profiles for all app identifiers
    app_identifiers.each do |app_identifier|
      match(
        type: "development", 
        app_identifier: app_identifier,
        readonly: false, # Regenerate provisioning profile if needed
        force_for_new_devices: true,
        clone_branch_directly: true
      )
    end

    UI.message("Development provisioning profiles updated.")
  end  

  desc "Create a temporary keychain"
  lane :create_temp_keychain do
    UI.message("Creating a temporary keychain...")

    create_keychain(
      name: "temp_keychain",
      password: ENV["KEYCHAIN_PASSWORD"],
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: true
    )

    UI.message("Temporary keychain created successfully.")
  end

  desc "Synchronize certificates and provisioning profiles"
  lane :setup_provisioning_profiles do
    required_vars = ["GIT_ACCESS_TOKEN", "GIT_URL", "ITC_TEAM_ID", "ITC_TEAM_NAME", "KEYCHAIN_PASSWORD"]
    ensure_env_vars(required_vars)
    
    UI.message("Setting up provisioning profiles...")
    
    # Iterate over app identifiers to fetch provisioning profiles
    app_identifiers.each do |app_identifier|
      with_retry(3, "Fetching provisioning profile for #{app_identifier}") do
        UI.message("Fetching provisioning profile for #{app_identifier}...")
        match(
          git_basic_authorization: ENV["GIT_ACCESS_TOKEN"],
          git_url: ENV["GIT_URL"],
          type: "appstore",
          clone_branch_directly: true,
          platform: "ios",
          app_identifier: app_identifier,
          team_id: ENV["ITC_TEAM_ID"],
          team_name: ENV["ITC_TEAM_NAME"],
          readonly: true,
          keychain_name: "temp_keychain",
          keychain_password: ENV["KEYCHAIN_PASSWORD"]
        )
        log_success("Successfully fetched provisioning profile for #{app_identifier}")
      end
    end
    
    log_success("All provisioning profiles set up")
  end

  desc "Fetch development certificates and provisioning profiles for Mac Catalyst"
  lane :fetch_dev_profiles_catalyst do
    match(
      type: "development",
      platform: "catalyst",
      app_identifier: app_identifiers,
      readonly: true,
      clone_branch_directly: true
    )
  end

  desc "Fetch App Store certificates and provisioning profiles for Mac Catalyst"
  lane :fetch_appstore_profiles_catalyst do
    match(
      type: "appstore",
      platform: "catalyst",
      app_identifier: app_identifiers,
      readonly: true,
      clone_branch_directly: true
    )
  end

  desc "Setup provisioning profiles for Mac Catalyst"
  lane :setup_catalyst_provisioning_profiles do
    app_identifiers.each do |app_identifier|
      match(
        type: "development",
        platform: "catalyst",
        app_identifier: app_identifier,
        readonly: false,
        force_for_new_devices: true,
        clone_branch_directly: true
      )

      match(
        type: "appstore",
        platform: "catalyst",
        app_identifier: app_identifier,
        readonly: false,
        clone_branch_directly: true
      )
    end
  end

  desc "Clear derived data"
  lane :clear_derived_data_lane do
    UI.message("Clearing derived data...")
    clear_derived_data
  end

  desc "Increment build number"
  lane :increment_build_number_lane do
    UI.message("Incrementing build number to current timestamp...")
    
    # Set the new build number
    increment_build_number(
      xcodeproj: "ios/BlueWallet.xcodeproj", 
      build_number: ENV["NEW_BUILD_NUMBER"]
    )
  
    UI.message("Build number set to: #{ENV['NEW_BUILD_NUMBER']}")
  end

  desc "Install CocoaPods dependencies"
  lane :install_pods do
    UI.message("Installing CocoaPods dependencies...")
    cocoapods(podfile: "ios/Podfile",
      try_repo_update_on_error: true,
      repo_update: true,
      
      clean_install: true)
  end


  desc "Upload IPA to TestFlight"
  lane :upload_to_testflight_lane do

  branch_name = ENV['BRANCH_NAME'] || "unknown-branch"
  last_commit_message = ENV['LATEST_COMMIT_MESSAGE'] || "No commit message found"


  changelog = <<~CHANGELOG
    Build Information:
  CHANGELOG

  # Include the branch name only if it is not 'master'
  if branch_name != 'master'
    changelog += <<~CHANGELOG
      - Branch: #{branch_name}
    CHANGELOG
  end

  changelog += <<~CHANGELOG
    - Commit: #{last_commit_message}
  CHANGELOG

  ipa_path = ENV['IPA_OUTPUT_PATH']
  
  if ipa_path.nil? || ipa_path.empty? || !File.exist?(ipa_path)
    UI.user_error!("IPA file not found at path: #{ipa_path}")
  end

  UI.message("Uploading IPA to TestFlight from path: #{ipa_path}")
  UI.message("Changelog:\n#{changelog}")


  upload_to_testflight(
    api_key_path: "./appstore_api_key.json",
    ipa: ipa_path,
    skip_waiting_for_build_processing: true,
    changelog: changelog
  )

  UI.success("Successfully uploaded IPA to TestFlight!")
end

desc "Upload iOS source maps to Bugsnag"
lane :upload_bugsnag_sourcemaps do
  bugsnag_api_key = ENV['BUGSNAG_API_KEY']
  bugsnag_release_stage = ENV['BUGSNAG_RELEASE_STAGE'] || "production"
  version = ENV['PROJECT_VERSION']
  build_number = ENV['NEW_BUILD_NUMBER']

  UI.user_error!("BUGSNAG_API_KEY environment variable is missing") if bugsnag_api_key.nil?
  UI.user_error!("PROJECT_VERSION environment variable is missing") if version.nil?
  UI.user_error!("NEW_BUILD_NUMBER environment variable is missing") if build_number.nil?

  ios_sourcemap = "./ios/build/Build/Products/Release-iphonesimulator/main.jsbundle.map"

  if File.exist?(ios_sourcemap)
    UI.message("Uploading iOS source map to Bugsnag...")
    bugsnag_sourcemaps_upload(
      api_key: bugsnag_api_key,
      source_map: ios_sourcemap,
      minified_file: "./ios/main.jsbundle",
      code_bundle_id: "#{version}-#{build_number}",
      release_stage: bugsnag_release_stage,
      app_version: version
    )
    UI.success("iOS source map uploaded successfully.")
  else
    UI.error("iOS source map not found at #{ios_sourcemap}")
  end
end

  desc "Build the iOS app"
  lane :build_app_lane do
    Dir.chdir(project_root) do
      UI.message("Building the application from: #{Dir.pwd}")

      workspace_path = File.join(project_root, "ios", "BlueWallet.xcworkspace")
      export_options_path = File.join(project_root, "ios", "export_options.plist")

      clear_derived_data_lane
      
      UI.message("\033[1;34m==================== FASTLANE BUILD DEBUG ====================\033[0m")
      
      # Comprehensive environment check
      UI.message("\033[1;36mEnvironment Analysis:\033[0m")
      UI.message("  Project Root: #{project_root}")
      UI.message("  Current Directory: #{Dir.pwd}")
      UI.message("  Ruby Version: #{RUBY_VERSION}")
      UI.message("  Fastlane Version: #{Fastlane::VERSION}")
      UI.message("  Build Mode: Release (App Store)")
      
      # Ensure we're using the correct Xcode installation
      UI.message("\033[1;36mXcode Configuration:\033[0m")
      begin
        sh("sudo xcode-select -s /Applications/Xcode.app") rescue nil
        xcode_path = sh('xcode-select -p', log: false).strip
        xcode_version = sh('xcodebuild -version', log: false).strip
        UI.message("  Active Xcode Path: #{xcode_path}")
        UI.message("  Xcode Version: #{xcode_version}")
        
        # Check if Xcode path is valid
        if File.exist?(File.join(xcode_path, "usr/bin/xcodebuild"))
          UI.success("  \033[1;32mXcode installation is valid\033[0m")
        else
          UI.error("  \033[1;31mXcode installation appears invalid\033[0m")
        end
      rescue => e
        UI.error("  \033[1;31mError checking Xcode: #{e.message}\033[0m")
      end
      
      # Project structure analysis
      UI.message("\033[1;36mProject Structure Analysis:\033[0m")
      %w[
        ios/BlueWallet.xcworkspace
        ios/BlueWallet.xcodeproj
        ios/export_options.plist
        ios/Podfile
        ios/Podfile.lock
      ].each do |file_path|
        full_path = File.join(project_root, file_path)
        if File.exist?(full_path)
          UI.message("  \033[1;32m#{file_path} exists\033[0m")
          if file_path.end_with?('.plist')
            UI.message("    Content preview:")
            content = File.read(full_path).lines.first(10).join.strip
            UI.message("    #{content[0..200]}...")
          end
        else
          UI.error("  \033[1;31m#{file_path} missing\033[0m")
        end
      end
      
      # Environment variables check
      UI.message("\033[1;36mEnvironment Variables:\033[0m")
      %w[PROJECT_VERSION NEW_BUILD_NUMBER].each do |var|
        value = ENV[var]
        if value && !value.empty?
          UI.message("  \033[1;32m#{var}: #{value}\033[0m")
        else
          UI.error("  \033[1;31m#{var}: Not set or empty\033[0m")
        end
      end
      
      # Determine which iOS version to use
      UI.message("\033[1;36miOS Version Analysis:\033[0m")
      ios_version = determine_ios_version
      UI.message("  Selected iOS version: #{ios_version}")

      UI.message("\033[1;36mBuild Configuration:\033[0m")
      UI.message("  Workspace: #{workspace_path}")
      UI.message("  Export options: #{export_options_path}")
      UI.message("  Output directory: #{File.join(project_root, 'ios', 'build')}")
      UI.message("  Build type: Release (generic/platform=iOS)")
      
      # Comprehensive destination analysis
      UI.message("\033[1;33mBuild Destinations Analysis:\033[0m")
      begin
        destinations_output = sh("xcodebuild -workspace '#{workspace_path}' -scheme BlueWallet -showdestinations", log: false)
        UI.message("  Available destinations:")
        destinations_output.lines.each_with_index do |line, index|
          UI.message("    #{index + 1}. #{line.strip}") if line.strip.length > 0
        end
        
        # Analyze destination types
        ios_destinations = destinations_output.scan(/platform:iOS[^}]*/).length
        catalyst_destinations = destinations_output.scan(/Mac Catalyst/).length
        
        UI.message("  \033[1;36mDestination Summary:\033[0m")
        UI.message("    iOS destinations: #{ios_destinations}")
        UI.message("    Mac Catalyst destinations: #{catalyst_destinations}")
        
        if ios_destinations > 0
          UI.success("  \033[1;32miOS destinations available for release build\033[0m")
        else
          UI.important("  \033[1;33mNo iOS destinations found - may need to create simulators\033[0m")
        end
      rescue => e
        UI.error("  \033[1;31mFailed to get destinations: #{e.message}\033[0m")
        destinations_output = "Failed to get destinations: #{e.message}"
      end
      
      # Simulator runtime check for development/debugging
      UI.message("\033[1;33mSimulator Runtime Check (for debugging):\033[0m")
      begin
        runtimes = sh("xcrun simctl list runtimes", log: false)
        ios_runtimes = runtimes.scan(/iOS ([0-9.]+)/).flatten
        UI.message("  Available iOS runtimes: #{ios_runtimes.join(', ')}")
        
        devices = sh("xcrun simctl list devices iOS", log: false)
        iphone_count = devices.scan(/iPhone/).length
        UI.message("  Available iPhone simulators: #{iphone_count}")
      rescue => e
        UI.error("  \033[1;31mError checking simulators: #{e.message}\033[0m")
      end
      
      # Define the IPA output path before building
      ipa_directory = File.join(project_root, "ios", "build")
      ipa_name = "BlueWallet_#{ENV['PROJECT_VERSION']}_#{ENV['NEW_BUILD_NUMBER']}.ipa"
      ipa_path = File.join(ipa_directory, ipa_name)
      
      UI.message("\033[1;36mBuild Output Configuration:\033[0m")
      UI.message("  IPA Directory: #{ipa_directory}")
      UI.message("  IPA Name: #{ipa_name}")
      UI.message("  Full IPA Path: #{ipa_path}")
      
      # Ensure build directory exists
      FileUtils.mkdir_p(ipa_directory) unless Dir.exist?(ipa_directory)
      
      begin
        UI.message("🚀 Starting iOS Build Process...")
        UI.message("  Build Parameters:")
        UI.message("    Scheme: BlueWallet")
        UI.message("    Workspace: #{workspace_path}")
        UI.message("    Export Method: app-store")
        UI.message("    Export Options: #{export_options_path}")
        UI.message("    Output Directory: #{ipa_directory}")
        UI.message("    Output Name: #{ipa_name}")
        UI.message("    Build Logs: #{File.join(project_root, 'ios', 'build_logs')}")
        UI.message("    Destination: generic/platform=iOS")
        
        # Pre-build validation
        UI.message("🔍 Pre-Build Validation:")
        
        # Check workspace
        if File.exist?(workspace_path)
          UI.success("  ✅ Workspace exists")
        else
          UI.user_error!("  ❌ Workspace not found: #{workspace_path}")
        end
        
        # Check export options
        if File.exist?(export_options_path)
          UI.success("  ✅ Export options exist")
          # Read and validate export options
          begin
            export_content = File.read(export_options_path)
            UI.message("    Export options preview:")
            export_content.lines.first(5).each { |line| UI.message("      #{line.strip}") }
          rescue => e
            UI.error("    ⚠️ Could not read export options: #{e.message}")
          end
        else
          UI.user_error!("  ❌ Export options not found: #{export_options_path}")
        end
        
        build_ios_app(
          scheme: "BlueWallet",
          workspace: workspace_path,
          export_method: "app-store",
          export_options: export_options_path,
          output_directory: ipa_directory,
          output_name: ipa_name,
          buildlog_path: File.join(project_root, "ios", "build_logs")
          # Removed explicit destination - let Xcode determine the best destination for release builds
        )
        
        UI.success("\033[1;32miOS release build completed successfully!\033[0m")
        
      rescue => build_error
        UI.error("\033[1;31miOS release build failed!\033[0m")
        UI.error("  Error Class: #{build_error.class}")
        UI.error("  Error Message: #{build_error.message}")
        UI.error("  \033[1;31mError Backtrace:\033[0m")
        build_error.backtrace.first(5).each { |line| UI.error("    #{line}") }
        
        UI.message("\033[1;33mPost-Build Debugging:\033[0m")
        
        # Check for partial build artifacts
        build_logs_dir = File.join(project_root, "ios", "build_logs")
        if Dir.exist?(build_logs_dir)
          UI.message("  \033[1;36mBuild logs directory contents:\033[0m")
          Dir.entries(build_logs_dir).each do |file|
            next if file.start_with?('.')
            file_path = File.join(build_logs_dir, file)
            size = File.size(file_path) rescue 0
            UI.message("    #{file} (#{size} bytes)")
          end
        end
        
        # Check for xcarchive files
        archives = Dir.glob(File.join(Dir.home, "Library/Developer/Xcode/Archives/**/*.xcarchive"))
        if archives.any?
          UI.message("  \033[1;36mRecent archives found:\033[0m")
          archives.last(3).each { |archive| UI.message("    #{archive}") }
        end
        
        # If iOS build fails, check if we can build using available destinations
        if destinations_output.include?("Any iOS Device")
          UI.message("Retrying build without explicit destination...")
          build_ios_app(
            scheme: "BlueWallet",
            workspace: workspace_path,
            export_method: "app-store",
            export_options: export_options_path,
            output_directory: ipa_directory,
            output_name: ipa_name,
            buildlog_path: File.join(project_root, "ios", "build_logs")
          )
        else
          UI.user_error!("build_ios_app failed: #{build_error.message}")
        end
      end

      # Check for IPA path from both our defined path and fastlane's context
      ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH] || ipa_path
      
      # Ensure the directory exists
      FileUtils.mkdir_p(File.dirname(ipa_path)) unless Dir.exist?(File.dirname(ipa_path))
      
      if ipa_path && File.exist?(ipa_path)
        UI.message("IPA successfully found at: #{ipa_path}")
      else
        # Try to find any IPA file as fallback
        Dir.chdir(project_root) do
          fallback_ipa = Dir.glob("**/*.ipa").first
          if fallback_ipa
            ipa_path = File.join(project_root, fallback_ipa)
            UI.message("Found fallback IPA at: #{ipa_path}")
          else
            UI.user_error!("No IPA file found after build")
          end
        end
      end
      
      # Set both environment variable and GitHub Actions output
      ENV['IPA_OUTPUT_PATH'] = ipa_path
      # Set both standard output format and the newer GITHUB_OUTPUT format
      sh("echo 'ipa_output_path=#{ipa_path}' >> $GITHUB_OUTPUT") if ENV['GITHUB_OUTPUT']
      sh("echo ::set-output name=ipa_output_path::#{ipa_path}")
      
      # Also write path to a file that can be read by subsequent steps
      ipa_path_file = "#{ipa_directory}/ipa_path.txt"
      File.write(ipa_path_file, ipa_path)
      UI.success("Saved IPA path to: #{ipa_path_file}")
    end
  end

  desc "Delete temporary keychain"
  lane :delete_temp_keychain do
    UI.message("Deleting temporary keychain...")
    
    delete_keychain(
      name: "temp_keychain"
    ) if File.exist?(File.expand_path("~/Library/Keychains/temp_keychain-db"))
    
    UI.message("Temporary keychain deleted successfully.")
  end

  # Helper method to determine which iOS version to use
  # Updated for macOS-15 compatibility (defaults to iOS 17.5 for broader compatibility)
  private_lane :determine_ios_version do
    UI.message("\033[1;33mDetermining iOS Version for Release Build:\033[0m")
    
    begin
      runtimes_output = sh("xcrun simctl list runtimes 2>&1", log: false)
      UI.message("  \033[1;32mSuccessfully retrieved simulator runtimes\033[0m")
      
      # Debug: Show all runtimes
      UI.message("  \033[1;36mAll available runtimes:\033[0m")
      runtimes_output.lines.first(10).each_with_index do |line, index|
        UI.message("    #{index + 1}. #{line.strip}")
      end
      
    rescue => e
      UI.error("  \033[1;31mFailed to get simulator runtimes: #{e.message}\033[0m")
      runtimes_output = ""
    end
    
    if runtimes_output.include?("iOS")
      UI.message("  \033[1;36miOS runtimes detected\033[0m")
      
      begin
        ios_versions = runtimes_output.scan(/iOS ([0-9.]+)/)
                                     .flatten
                                     .map { |v| Gem::Version.new(v) }
                                     .sort
                                     .reverse
        
        UI.message("  \033[1;36mParsed iOS versions:\033[0m")
        ios_versions.each_with_index do |version, index|
          UI.message("    #{index + 1}. iOS #{version}")
        end
        
        if ios_versions.any?
          latest_version = ios_versions.first.to_s
          UI.success("  \033[1;32mSelected iOS version for release: #{latest_version}\033[0m")
          
          # Additional validation
          if Gem::Version.new(latest_version) >= Gem::Version.new("17.0")
            UI.success("    \033[1;32mVersion is compatible for release builds (17.0+)\033[0m")
          else
            UI.important("    \033[1;33mVersion is older than 17.0, may have compatibility issues\033[0m")
          end
          
          latest_version
        else
          UI.important("  \033[1;33mNo iOS versions could be parsed from runtime output\033[0m")
          UI.message("  \033[1;36mUsing fallback version for release: 17.5\033[0m")
          "17.5"
        end
      rescue => e
        UI.error("  \033[1;31mError parsing iOS versions: #{e.message}\033[0m")
        UI.message("  \033[1;36mUsing fallback version for release: 17.5\033[0m")
        "17.5"
      end
    else
      UI.important("  \033[1;33mNo iOS runtimes found in simulator list\033[0m")
      UI.message("  \033[1;36mRuntime output preview:\033[0m")
      runtimes_output.lines.first(5).each { |line| UI.message("    #{line.strip}") }
      UI.message("  \033[1;36mUsing fallback version for release: 17.5\033[0m")
      "17.5"
    end
  end
end

# ===========================
#       Global Lanes
# ===========================

desc "Deploy to TestFlight"
lane :deploy do |options|
  UI.message("Starting deployment process...")

  update_wwdr_certificate
  setup_app_store_connect_api_key
  setup_provisioning_profiles
  clear_derived_data_lane
  increment_build_number_lane

  unless File.directory?("Pods")
    install_pods
  end

  build_app_lane
  upload_to_testflight_lane

  delete_keychain(name: "temp_keychain")

  last_commit = last_git_commit
  already_built_flag = ".already_built_#{last_commit[:sha]}"
  File.write(already_built_flag, Time.now.to_s)
end

desc "Update release notes for App Store versions (iOS or Mac Catalyst)"
lane :release_notes do |options|
  require 'spaceship'

  app = Spaceship::ConnectAPI::App.find(app_identifiers.first)

  UI.user_error!("Could not find the app with identifier: #{app_identifiers.first}") unless app

  platform_option = options[:platform]
  
  if platform_option
    platform = case platform_option.to_s.downcase
      when "ios"
        UI.message("Using platform from options: iOS")
        Spaceship::ConnectAPI::Platform::IOS
      when "catalyst", "mac_catalyst", "mac-catalyst"
        UI.message("Using platform from options: Mac Catalyst")
        UI.message("Using Spaceship::ConnectAPI::Platform::MAC_OS for Mac Catalyst")
        Spaceship::ConnectAPI::Platform::MAC_OS
      else
        UI.user_error!("Invalid platform option: #{platform_option}")
      end
  else
    platform_selection = UI.select("Select platform for release notes:", ["iOS", "Mac Catalyst"])
    
    platform = case platform_selection
      when "iOS"
        UI.message("Selected platform: iOS")
        Spaceship::ConnectAPI::Platform::IOS
      when "Mac Catalyst"
        UI.message("Selected platform: Mac Catalyst")
        UI.message("Using Spaceship::ConnectAPI::Platform::MAC_OS for Mac Catalyst")
        Spaceship::ConnectAPI::Platform::MAC_OS
      else
        UI.user_error!("Invalid platform selection")
      end
  end

  retries = 5
  begin
    rejected_version = nil
    UI.message("Checking for Developer Rejected version for platform: #{platform}")
    
    begin
      filter = {
        appStoreState: "DEVELOPER_REJECTED",
        platformString: platform.to_s
      }
      
      app.get_app_store_versions(filter: filter).each do |version|
        rejected_version = version
        UI.message("Found rejected version: #{version.version_string}")
        break
      end
    rescue => e
      UI.error("Error fetching Developer Rejected versions: #{e.message}")
      UI.message("Debug info: Platform type: #{platform.class}, Value: #{platform}")
    end

    if rejected_version
      UI.success("Found 'Developer Rejected' version: #{rejected_version.version_string}. This will be the target for updates.")
      prepare_version = rejected_version
    else
      UI.message("No Developer Rejected version found. Checking for version in edit mode or waiting for review...")
      
      begin
        prepare_version = app.get_edit_app_store_version(platform: platform)
        if prepare_version
          UI.message("Found version in edit mode: #{prepare_version.version_string}")
        else
          UI.message("No version in edit mode found")
        end
      rescue => e
        UI.error("Error fetching edit app store version: #{e.message}")
        prepare_version = nil
      end
      
      if prepare_version.nil?
        UI.message("Checking for version in Waiting for Review status...")
        begin
          waiting_filter = {
            platformString: platform.to_s,
            appStoreState: "WAITING_FOR_REVIEW"
          }
          
          waiting_versions = app.get_app_store_versions(filter: waiting_filter)
          if waiting_versions && !waiting_versions.empty?
            prepare_version = waiting_versions.first
            UI.success("Found version in Waiting for Review status: #{prepare_version.version_string}")
          else
            UI.message("No version in Waiting for Review status found")
          end
        rescue => e
          UI.error("Error fetching Waiting for Review versions: #{e.message}")
        end
      end
      
      if prepare_version.nil?
        UI.message("Looking for any in-flight version...")
        begin
          all_versions = app.get_app_store_versions(filter: { platformString: platform.to_s })
          UI.message("Found #{all_versions.count} versions for platform #{platform}:")
          
          all_versions.each do |version|
            state = app_store_state_readable(version.app_store_state)
            UI.message("  - Version: #{version.version_string}, State: #{state}")
          end
          
          editable_states = ["PREPARE_FOR_SUBMISSION", "WAITING_FOR_REVIEW", "REJECTED", "METADATA_REJECTED", "DEVELOPER_REJECTED"]
          editable_version = all_versions.find { |v| editable_states.include?(v.app_store_state) }
          
          if editable_version
            prepare_version = editable_version
            UI.success("Using editable version: #{prepare_version.version_string} (#{app_store_state_readable(prepare_version.app_store_state)})")
          elsif all_versions.count > 0
            latest_version = all_versions.sort_by { |v| Gem::Version.new(v.version_string) }.last
            UI.message("Latest version: #{latest_version.version_string} (#{app_store_state_readable(latest_version.app_store_state)})")
          end
        rescue => e
          UI.error("Error listing versions: #{e.message}")
        end
      end
      
      if prepare_version.nil?
        UI.message("No editable version found.")
        
        create_new_version = UI.confirm("Would you like to create a new version?")
        
        if create_new_version
          begin
            UI.message("Fetching latest version for platform: #{platform}")
            latest_version = app.get_latest_version(platform: platform)
            if latest_version
              UI.message("Latest version: #{latest_version.version_string}")
              new_version_number = (latest_version.version_string.to_f + 0.1).round(1).to_s
            else
              UI.message("No latest version found. Using 1.0 as base")
              new_version_number = "1.0"
            end
            
            UI.message("Creating new version: #{new_version_number} for platform: #{platform_selection || platform_option}")
            prepare_version = app.create_version!(platform: platform, version_string: new_version_number)
            UI.message("Created new version: #{new_version_number}")
          rescue => e
            UI.error("Failed to create new version: #{e.message}")
            UI.user_error!("Failed to create version. Make sure your app is configured for Mac Catalyst in App Store Connect.")
          end
        else
          UI.user_error!("No editable version found and user chose not to create one. Aborting.")
        end
      else
        UI.message("Using version #{prepare_version.version_string} in state: #{app_store_state_readable(prepare_version.app_store_state)}")
      end
    end
  rescue => e
    retries -= 1
    if retries > 0
      delay = 20
      UI.message("Cannot find app version info... Retrying after #{delay} seconds (remaining: #{retries})")
      UI.error("Error details: #{e.message}")
      sleep(delay)
      retry
    else
      UI.user_error!("Failed to fetch or create the app version: #{e.message}")
    end
  end

  localized_metadata = prepare_version.get_app_store_version_localizations
  enabled_locales = localized_metadata.map(&:locale)
  release_notes_text = options[:release_notes]
  
  if release_notes_text.nil? || release_notes_text.strip.empty?
    existing_release_notes = nil
    
    en_us_localization = localized_metadata.find { |loc| loc.locale == 'en-US' }
    if en_us_localization && en_us_localization.whats_new && !en_us_localization.whats_new.strip.empty?
      existing_release_notes = en_us_localization.whats_new
      UI.success("Found existing release notes in App Store Connect!")
    else
      localized_metadata.each do |loc|
        if loc.whats_new && !loc.whats_new.strip.empty?
          existing_release_notes = loc.whats_new
          UI.success("Found existing release notes in App Store Connect for locale: #{loc.locale}")
          break
        end
      end
    end
    
    ios_release_notes_path = "metadata/ios/en-US/release_notes.txt"
    project_release_notes_path = "../release-notes.txt"
    
    ios_notes_exist = File.exist?(ios_release_notes_path)
    project_notes_exist = File.exist?(project_release_notes_path)
    
    options_list = []
    
    if existing_release_notes
      options_list << "View/Edit existing App Store notes"
    end
    
    options_list += [
      "Enter manually",
      "Use clipboard content"
    ]
    
    if project_notes_exist
      options_list << "Use release-notes.txt file"
    end
    
    if ios_notes_exist
      options_list << "Use iOS metadata release notes"
    end
    
    selection = UI.select("Select a source for release notes:", options_list)
    
    case selection
    when "View/Edit existing App Store notes"
      UI.message("Existing release notes:")
      UI.message("-" * 50)
      UI.message(existing_release_notes)
      UI.message("-" * 50)
      
      edit_choice = UI.select("Do you want to edit these notes or use as-is?:", [
        "Use as-is",
        "Edit notes"
      ])
      
      if edit_choice == "Edit notes"
        require 'tempfile'
        temp_file = Tempfile.new('release_notes')
        temp_file.write(existing_release_notes)
        temp_file.close
        
        editor = ENV['EDITOR'] || 'nano'
        system("#{editor} #{temp_file.path}")
        
        release_notes_text = File.read(temp_file.path)
        temp_file.unlink
        
        UI.message("Edited release notes:")
        UI.message("-" * 50)
        UI.message(release_notes_text.length > 500 ? "#{release_notes_text[0..500]}..." : release_notes_text)
        UI.message("-" * 50)
        
        unless UI.confirm("Use these edited notes?")
          UI.user_error!("User canceled edited notes. Aborting.")
        end
      else
        release_notes_text = existing_release_notes
      end
    
    when "Enter manually"
      release_notes_text = UI.input("Enter the release notes:")
      if release_notes_text.nil? || release_notes_text.strip.empty?
        UI.user_error!("No release notes provided. Aborting.")
      end
      
    when "Use clipboard content"
      require 'open3'
      stdout, stderr, status = Open3.capture3("pbpaste")
      
      if !status.success? || stdout.strip.empty?
        UI.user_error!("Failed to get clipboard content or clipboard is empty")
      end
      
      UI.message("Clipboard content preview:")
      UI.message("-" * 50)
      UI.message(stdout.length > 500 ? "#{stdout[0..500]}..." : stdout)
      UI.message("-" * 50)
      
      unless UI.confirm("Use this clipboard content for release notes?")
        UI.user_error!("User canceled clipboard content usage. Aborting.")
      end
      
      release_notes_text = stdout
      
    when "Use iOS metadata release notes"
      release_notes_text = File.read(ios_release_notes_path)
      
      UI.message("iOS metadata release notes preview:")
      UI.message("-" * 50)
      UI.message(release_notes_text.length > 500 ? "#{release_notes_text[0..500]}..." : release_notes_text)
      UI.message("-" * 50)
      
      unless UI.confirm("Use this content from iOS metadata release notes?")
        UI.user_error!("User canceled file content usage. Aborting.")
      end
      
    when "Use release-notes.txt file"
      release_notes_path = "../release-notes.txt"
      
      unless File.exist?(release_notes_path)
        UI.error("Release notes file does not exist at path: #{release_notes_path}")
        UI.user_error!("No release-notes.txt file found. Aborting.")
      end
      
      release_notes_text = File.read(release_notes_path)
      
      UI.message("release-notes.txt content preview:")
      UI.message("-" * 50)
      UI.message(release_notes_text.length > 500 ? "#{release_notes_text[0..500]}..." : release_notes_text)
      UI.message("-" * 50)
      
      unless UI.confirm("Use this content from release-notes.txt?")
        UI.user_error!("User canceled file content usage. Aborting.")
      end
    end
  end
  
  if release_notes_text.nil? || release_notes_text.strip.empty?
    UI.user_error!("No release notes content available. Aborting.")
  end

  localized_release_notes = {
    'en-US' => release_notes_text,
    'ar-SA' => release_notes_text,
    'zh-Hans' => release_notes_text,
    'hr' => release_notes_text,
    'da' => release_notes_text,
    'nl-NL' => release_notes_text,
    'fi' => release_notes_text,
    'fr-FR' => release_notes_text,
    'de-DE' => release_notes_text,
    'el' => release_notes_text,
    'he' => release_notes_text,
    'hu' => release_notes_text,
    'it' => release_notes_text,
    'ja' => release_notes_text,
    'ms' => release_notes_text,
    'nb' => release_notes_text,
    'no' => release_notes_text,
    'pl' => release_notes_text,
    'pt-BR' => release_notes_text,
    'pt-PT' => release_notes_text,
    'ro' => release_notes_text,
    'ru' => release_notes_text,
    'es-MX' => release_notes_text,
    'es-ES' => release_notes_text,
    'sv' => release_notes_text,
    'th' => release_notes_text,
  }

  if platform == Spaceship::ConnectAPI::Platform::MAC_OS
    UI.message("Mac Catalyst selected - using only en-US localization")
    localized_release_notes = { 'en-US' => release_notes_text }
  end

  localized_release_notes = localized_release_notes.select { |locale, _| enabled_locales.include?(locale) }

  UI.message("Review the following release notes updates:")
  localized_release_notes.each do |locale, notes|
    UI.message("Locale: #{locale} - Notes: #{notes}")
  end

  force_yes = options && options.is_a?(Hash) && options[:force_yes] == true
  
  unless force_yes
    confirm = UI.confirm("Do you want to proceed with these release notes updates?")
    UI.user_error!("User aborted the lane.") unless confirm
  end

  localized_release_notes.each do |locale, notes|
    app_store_version_localization = localized_metadata.find { |loc| loc.locale == locale }
    if app_store_version_localization
      app_store_version_localization.update(attributes: { "whats_new" => notes })
    else
      UI.error("No localization found for locale #{locale}")
    end
  end
end
