import "../../fastlane/RootDirectoryFastfile"
import "../../fastlane/EnvFastfile"
import "../../fastlane/DotEnvFastfile"
import "../../fastlane/ConfigurationFastfile"
import "../../fastlane/BuildDirectoryFastfile"
import "../../fastlane/JsBundleFastfile"
import "../../fastlane/CodePushFastfile"
import "../../fastlane/PoEditorFastfile"
import "../../fastlane/SentryFastfile"
import "../../fastlane/ResizeImageFastfile"
import "../../fastlane/AwsFastfile"

NATIVE_BUILD_DIRECTORY = "#{IOS_BUILD_DIRECTORY}/native"
DERIVED_DATA_DIRECTORY = "#{NATIVE_BUILD_DIRECTORY}/derivedData"
IPA_DIRECTORY = "#{NATIVE_BUILD_DIRECTORY}/ipa"
IPA_NAME = "orchardGo.ipa"
DSYM_MAPPING_FILE = "#{IPA_DIRECTORY}/orchardGo.app.dSYM.zip"
PROVISIONING_PROFILE_FILE = "#{NATIVE_BUILD_DIRECTORY}/provisioningProfile/profile.mobileprovision"
KEYCHAIN_DIRECTORY = "#{NATIVE_BUILD_DIRECTORY}/keychain"
KEYCHAIN_PASSWORD = "1111"
SIMULATOR_APP_DIRECTORY = "#{NATIVE_BUILD_DIRECTORY}/simulatorApp"
GOOGLE_SERVICE_INFO_PLIST_FILE = '/GoogleService-Info.plist'
DEVICES_FILE = "/ios/fastlane/devices.txt"

WORKSPACE_NAME = 'orchardgo.xcworkspace'
PROJECT_NAME = 'orchardgo.xcodeproj'
PROJECT_SCHEME = 'orchardgo'
PROJECT_DIRECTORY_NAME = 'orchardgo'

APS_ENVIRONMENT_ENTITLEMENT_DEVELOPMENT = 'development'
APS_ENVIRONMENT_ENTITLEMENT_PRODUCTION = 'production'

TARGET_SIMULATOR = 'simulator'
TARGET_DEVICE = 'device'

EXPORT_METHOD_APPSTORE = "app-store"
EXPORT_METHOD_ADHOC = "ad-hoc"


LAUNCHER_IMAGE_CONFIGURATIONS = [
  {
    "size": 1024,
    "xcode_size_property": 1024,
    "xcode_idiom_property": "ios-marketing",
    "xcode_scale_property": "1x"
  },
  {
    "size": 180,
    "xcode_size_property": 60,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "3x"
  },
  {
    "size": 120,
    "xcode_size_property": 40,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "3x"
  },
  {
    "size": 120,
    "xcode_size_property": 60,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "2x"
  },
  {
    "size": 87,
    "xcode_size_property": 29,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "3x"
  },
  {
    "size": 80,
    "xcode_size_property": 40,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "2x"
  },
  {
    "size": 60,
    "xcode_size_property": 20,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "3x"
  },
  {
    "size": 58,
    "xcode_size_property": 29,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "2x"
  },
  {
    "size": 40,
    "xcode_size_property": 20,
    "xcode_idiom_property": "iphone",
    "xcode_scale_property": "2x"
  }
]
SPLASH_IMAGE_CONFIGURATIONS = [
  {
    "size": 180,
    "xcode_idiom_property": "universal",
    "xcode_scale_property": "1x"
  },
  {
    "size": 360,
    "xcode_idiom_property": "universal",
    "xcode_scale_property": "2x"
  },
  {
    "size": 540,
    "xcode_idiom_property": "universal",
    "xcode_scale_property": "3x"
  }
]

default_platform(:ios)

platform :ios do

  before_all do
    init_root_project_directory_path(File.expand_path('../..'))
    init_environment_variables()
    ensure_configuration()
  end

  desc "Build app for simulator"
  lane :build_app_for_simulator do
    derived_data_directory = "#{root_project_directory_path()}/#{SIMULATOR_APP_DIRECTORY}"
    xcodebuild(
      workspace: WORKSPACE_NAME,
      scheme: PROJECT_SCHEME,
      xcargs: "-configuration Release -sdk 'iphonesimulator' -destination 'generic/platform=iOS Simulator' -derivedDataPath #{derived_data_directory}"
    )
    original_simulator_app_file = "#{derived_data_directory}/Build/Products/Release-iphonesimulator/#{application_name()}.app"
    renamed_simulator_app_file = "#{derived_data_directory}/Build/Products/Release-iphonesimulator/#{brand()}.app"
    unless original_simulator_app_file == renamed_simulator_app_file
      sh "mv #{original_simulator_app_file} #{renamed_simulator_app_file}"
    end
    # test_simulator_app
  end

  def configure_xcode()
    optional_xcode_path = xcode_path()
    if optional_xcode_path.nil? or optional_xcode_path.length == 0
      raise "xcode_path() is not set"
    end
    xcode_select(optional_xcode_path)
  end

  def configure_native_project(target)
    configure_xcode()
    clean_build_directory()
    deny_metro_bundler_autolaunch()

    set_ios_version_number
    set_aps_environment_entitlement
    create_native_resources

    if target == TARGET_DEVICE
      set_ios_build_number
      set_team_id
    end

    prepare_source_code_for_js_bundle()
  end

  desc "Set iOS version number"
  lane :set_ios_version_number do
    increment_version_number(
      version_number: application_version()
    )
  end

  desc "Set iOS build number"
  lane :set_ios_build_number do
    increment_build_number(
      build_number: build_number()
    )
  end

  desc "Set application name"
  lane :legacy_set_application_name do
    update_info_plist(
      xcodeproj: PROJECT_NAME,
      scheme: PROJECT_SCHEME,
      display_name: application_name()
    )
  end

  desc "Set Bundle Identifier (PRODUCT_BUNDLE_IDENTIFIER)"
  lane :legacy_set_bundle_identifier do
    update_app_identifier(
      xcodeproj: PROJECT_NAME,
      plist_path: "#{PROJECT_DIRECTORY_NAME}/Info.plist",
      app_identifier: ios_bundle_id()
    )
  end

  desc "Set launcher icon"
  lane :set_launcher_icon do
    create_images(
      launcher_icon(),
      "./ios/#{PROJECT_DIRECTORY_NAME}/Images.xcassets/AppIcon.appiconset",
      "AppIcon",
      LAUNCHER_IMAGE_CONFIGURATIONS
    )
  end

  desc "Set splash screen"
  lane :set_splash_screen do
    set_splash_screen_image()
    set_splash_screen_background()
  end

  def set_splash_screen_image()
    create_images(
      splash_image(),
      "./ios/#{PROJECT_DIRECTORY_NAME}/Images.xcassets/splash.imageset",
      "splash",
      SPLASH_IMAGE_CONFIGURATIONS
    )
  end

  def create_images(
    relative_image_path_to_read,
    relative_directory_path_to_write,
    image_name_to_write,
    image_configurations)
    absolute_image_path_to_read = "#{root_project_directory_path()}/#{relative_image_path_to_read}"
    absolute_directory_path_to_write = "#{root_project_directory_path()}/#{relative_directory_path_to_write}"
    FileUtils.rm_rf(absolute_directory_path_to_write)
    FileUtils.mkdir_p(absolute_directory_path_to_write)
    content_images = []
    image_configurations.each { |configuration|
      size = configuration[:size]
      idiom = configuration[:xcode_idiom_property]
      scale = configuration[:xcode_scale_property]

      content_image = {
        "idiom" => idiom,
        "scale" => scale
      }

      image_name_suffix = ""
      if configuration.has_key?(:xcode_size_property)
        xcode_size = configuration[:xcode_size_property].to_s
        content_image["size"] = "#{xcode_size}x#{xcode_size}"
        image_name_suffix = "-#{size.to_s}x#{size.to_s}"
      elsif scale != "1x"
        image_name_suffix = "@#{scale}"
      end
      image_name = "#{image_name_to_write}#{image_name_suffix}.png"
      content_image["filename"] = "#{image_name}"

      content_images << content_image

      absolute_image_path_to_write = "#{absolute_directory_path_to_write}/#{image_name}"
      resize_image(
        absolute_image_path_to_read,
        size,
        size,
        absolute_image_path_to_write
      )
    }

    contents = {
      'images' => content_images,
      'info' => {
        'version' => 1,
        'author' => 'fastlane'
      }
    }

    contents_json = JSON.pretty_generate(contents)
    absolute_contents_path_to_write = "#{root_project_directory_path()}/#{relative_directory_path_to_write}/Contents.json"
    File.open(absolute_contents_path_to_write, 'w') { |file| file.write(contents_json) }
  end

  def set_splash_screen_background()
    background_color_directory_path = "../#{PROJECT_DIRECTORY_NAME}/Colors.xcassets/splashBackground.colorset"
    FileUtils.rm_rf(background_color_directory_path)
    FileUtils.mkdir_p(background_color_directory_path)

    color = splash_background_color()
    red = color[1..2]
    green = color[3..4]
    blue = color[5..6]

    contents_json_file_path = "../#{PROJECT_DIRECTORY_NAME}/Colors.xcassets/splashBackground.colorset/Contents.json"
    contents_json_file_content = [
      "{",
      "  \"info\" : {",
      "    \"version\" : 1,",
      "    \"author\" : \"fastlane\"",
      "  },",
      "  \"colors\" : [",
      "    {",
      "      \"idiom\" : \"universal\",",
      "      \"color\" : {",
      "        \"color-space\" : \"srgb\",",
      "        \"components\" : {",
      "          \"red\" : \"0x#{red}\",",
      "          \"alpha\" : \"1.000\",",
      "          \"blue\" : \"0x#{blue}\",",
      "          \"green\" : \"0x#{green}\"",
      "        }",
      "      }",
      "    }",
      "  ]",
      "}"
    ].join("\n")
    File.open(contents_json_file_path, 'w') { |file| file.write(contents_json_file_content) }
  end

  desc "Set scheme"
  lane :legacy_set_scheme do
    scheme = redirection_scheme()
    update_info_plist(
      xcodeproj: PROJECT_NAME,
      scheme: PROJECT_SCHEME,
      block: proc do |plist|
        plist["CFBundleURLTypes"] = [
          {"CFBundleURLSchemes": [scheme]},
          {"CFBundleURLSchemes": [ios_bundle_id()]}
        ]
      end
    )
  end

  desc "Set environment"
  lane :legacy_set_environment do
    update_info_plist(
      xcodeproj: PROJECT_NAME,
      scheme: PROJECT_SCHEME,
      block: proc do |plist|
        plist["Brand"] = brand()
        plist["Environment"] = environment()
      end
    )
  end

  desc "Set Team Id"
  lane :set_team_id do
    update_project_team(
      path: PROJECT_NAME,
      teamid: app_store_team_id()
    )
  end

  desc "Set APS Environment Entitlement"
  lane :set_aps_environment_entitlement do
    update_plist(
      plist_path: "#{PROJECT_DIRECTORY_NAME}/#{PROJECT_SCHEME}.entitlements",
      block: proc do |plist|
        plist['aps-environment'] = APS_ENVIRONMENT_ENTITLEMENT_PRODUCTION
      end
    )
  end

  desc "Set application name for login permission"
  lane :legacy_set_application_name_for_login_permission do
    default_brand = "orchard"
    search_string = "PRODUCT_NAME = #{default_brand}"
    replace_string = "PRODUCT_NAME = #{application_name()}"
    file = "#{root_project_directory_path()}/ios/#{PROJECT_NAME}/project.pbxproj"

    original_search_strings_count = File.readlines(file).select{ |item| item.include? search_string }.size
    if original_search_strings_count == 0
      raise "File '#{file} does not contain string '#{search_string}'. It should exist."
    end

    File.write(file,File.open(file,&:read).gsub(search_string,replace_string))

    actual_search_strings_count = File.readlines(file).select{ |item| item.include? search_string }.size
    if brand() != default_brand && actual_search_strings_count != 0
      raise "File '#{file} contains string '#{search_string}'. It should be replaced."
    end
    actual_replace_strings_count = File.readlines(file).select{ |item| item.include? replace_string }.size
    if actual_replace_strings_count != original_search_strings_count
      raise "Search string is not properly replaced."
    end
  end

  def build_number()
    if store() == STORE_STORE
      return get_latest_build_number_from_testflight() + 1
    else
      return 1
    end
  end

  def get_latest_build_number_from_testflight()
    build_number = 0
    api_key = get_app_store_connect_api_key()
    build_number = latest_testflight_build_number(
      version: application_version(),
      app_identifier: ios_bundle_id(),
      api_key: api_key,
      team_id: app_store_team_id(),
      initial_build_number: 0
    )
    return build_number
  end

  def create_ipa(export_method, original_profile_file)
    keychain_file = "#{root_project_directory_path()}/#{KEYCHAIN_DIRECTORY}/mobile_#{brand()}_#{environment()}.keychain"
    profile_file = "#{root_project_directory_path()}/#{PROVISIONING_PROFILE_FILE}"
    ipa_directory = "#{root_project_directory_path()}/#{IPA_DIRECTORY}"
    derived_data_directory = "#{root_project_directory_path()}/#{DERIVED_DATA_DIRECTORY}"

    profile_directory = File.dirname(profile_file)
    unless File.directory?(profile_directory)
      FileUtils.mkdir_p(profile_directory)
    end
    FileUtils.cp(original_profile_file, profile_file)
    File.chmod(0777, profile_file)

    create_keychain(
      path: keychain_file,
      password: KEYCHAIN_PASSWORD,
      unlock: true,
      timeout: 0,
      default_keychain: false,
      lock_when_sleeps: false
    )
    import_certificate(
      certificate_path: app_store_certificate_file_path(),
      certificate_password: app_store_certificate_password(),
      keychain_path: keychain_file,
      keychain_password: KEYCHAIN_PASSWORD
    )
    update_code_signing_settings(
      use_automatic_signing: false,
      path: PROJECT_NAME
    )
    update_project_provisioning(
      xcodeproj: PROJECT_NAME,
      profile: profile_file,
      code_signing_identity: app_store_certificate_signing_identity_name()
    )
    install_provisioning_profile(
      path: profile_file
    )
    build_app(
      scheme: PROJECT_SCHEME,
      workspace: WORKSPACE_NAME,
      clean: true,
      output_directory: ipa_directory,
      output_name: IPA_NAME,
      export_options: {
        method: export_method,
      },
      derived_data_path: derived_data_directory
    )
    test_ipa
  end

  desc "Upload binaries"
  lane :upload_binaries do |options|
    binaries_path = options[:binaries_path]
    target = options[:target]
    store = store()
    if store == STORE_STORE
      upload_binaries_to_testflight(binaries_path: binaries_path)
    elsif store === STORE_S3
      upload_binaries_to_aws_s3(
        binaries_path: binaries_path,
        target: target
      )
    else
      raise "no upload_binaries_to_* for store #{store}"
    end
  end

  desc "Upload binaries to AWS S3"
  lane :upload_binaries_to_aws_s3 do |options|
    binaries_path = options[:binaries_path]
    target = options[:target]
    aws_file_name = ""
    if target == TARGET_DEVICE
      aws_file_name = "ios_#{brand()}_#{environment()}.ipa"
    elsif target == TARGET_SIMULATOR
      aws_file_name = "ios_simulator_#{brand()}_#{environment()}.zip"
    else
      raise "unknown target #{target}"
    end
    prefix = aws_binaries_name_prefix()
    if !prefix.nil? && !prefix.empty?
      aws_file_name = "#{prefix}_#{aws_file_name}"
    end
    aws_s3_upload(
      aws_access_key(),
      aws_secret_access_key(),
      aws_region(),
      aws_bucket(),
      aws_file_name,
      binaries_path
    )
  end

  desc "Upload binaries to Testflight"
  lane :upload_binaries_to_testflight do |options|
    binaries_path = options[:binaries_path]
    api_key = get_app_store_connect_api_key()
    fix_transporter()
    upload_to_testflight(
      api_key: api_key,
      team_id: app_store_team_id(),
      ipa: binaries_path,
      skip_waiting_for_build_processing: true
    )
  end

  def fix_transporter()
    # commented for xcode 14
    # ENV['ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD'] = 'true'
  end

  def get_app_store_connect_api_key()
    return app_store_connect_api_key(
      key_id: app_store_api_key_id(),
      issuer_id: app_store_api_key_issuer_id(),
      key_filepath: app_store_api_key_file_path(),
      duration: 1200,
      in_house: false
    )
  end

  desc "Open simulator app in directory"
  lane :open_simulator_app_in_directory do
    directory = "#{root_project_directory_path()}/#{SIMULATOR_APP_DIRECTORY}/Build/Products/Release-iphonesimulator"
    sh("open #{directory}")
  end

  desc "Copy GoogleService-Info.plist file"
  lane :copy_google_service_info_plist_file do
    source_file = "#{root_project_directory_path()}/#{ios_google_services_info_plist_file()}"
    destination_file = "#{root_project_directory_path()}/ios/#{GOOGLE_SERVICE_INFO_PLIST_FILE}"
    FileUtils.cp(source_file, destination_file)
  end

  desc "Test IPA"
  lane :test_ipa do
    original_ipa = "#{root_project_directory_path()}/#{IPA_DIRECTORY}/#{IPA_NAME}"
    unzipped_ipa = "#{root_project_directory_path()}/#{IOS_TEST_DIRECTORY}/unzippedIpa"
    FileUtils.mkdir_p(unzipped_ipa)
    sh "unzip #{original_ipa} -d #{unzipped_ipa}"
    app_relative_path = "#{IOS_TEST_DIRECTORY}/unzippedIpa/Payload/#{application_name()}.app"
    bundle_relative_path = "#{app_relative_path}/#{IOS_BUNDLE_NAME}"
    # TODO: upgrade. uncomment
    # test_js_bundle(bundle_relative_path)
    test_entitlements(app_relative_path)
  end

  desc "Test Simulator app"
  lane :test_simulator_app do
    bundle_relative_path = "#{SIMULATOR_APP_DIRECTORY}/Build/Products/Release-iphonesimulator/#{brand()}.app/#{IOS_BUNDLE_NAME}"
    # TODO: upgrade. uncomment
    # test_js_bundle(bundle_relative_path)
  end

  def test_entitlements(app_file_path)
    Dir.chdir(root_project_directory_path()) do
      app_entitlements = sh "codesign -d --entitlements :- #{app_file_path}"
      profile_entitlements = sh "security cms -D -i #{app_file_path}/embedded.mobileprovision"
      entitlement_key = "<key>com.apple.developer.default-data-protection</key>"
      entitlement_value = "<string>NSFileProtectionComplete</string>"
      unless app_entitlements.include? entitlement_key and app_entitlements.include? entitlement_value
        raise "Application file does not contain entitlement: #{entitlement_key} #{entitlement_value}"
      end
      unless profile_entitlements.include? entitlement_key and profile_entitlements.include? entitlement_value
        raise "Embeded profile does not contain entitlement: #{entitlement_key} #{entitlement_value}"
      end
    end
  end

  def deny_metro_bundler_autolaunch()
    ENV["RCT_NO_LAUNCH_PACKAGER"] = "1"
  end

  desc "Creates xcconfig files"
  lane :create_xcconfigs_files do
    create_xcconfig_file("orchardgo.debug.xcconfig", "../../../Pods/Target Support Files/Pods-orchardgo/Pods-orchardgo.debug.xcconfig")
    create_xcconfig_file("orchardgo.release.xcconfig", "../../../Pods/Target Support Files/Pods-orchardgo/Pods-orchardgo.release.xcconfig")
  end

  def xcconfig_file_content_array(pods_xcconfig_relative_path)
    return [
      "#include \"#{pods_xcconfig_relative_path}\"",
      "PRODUCT_BUNDLE_IDENTIFIER = #{ios_bundle_id()}",
      "XCCONFIG_BUNDLE_ID = #{ios_bundle_id()}",
      "XCCONFIG_APP_NAME = #{application_name()}",
      "XCCONFIG_PRODUCT_NAME = #{application_name()}",
      "PRODUCT_NAME = #{application_name()}",
      "XCCONFIG_BRAND = #{brand()}",
      "XCCONFIG_ENVIRONMENT = #{environment()}",
      "XCCONFIG_LOGIN_REDIRECTION_SCHEME = #{redirection_scheme()}",
      "XCCONFIG_DYNAMIC_LINKS_SCHEME = #{ios_bundle_id()}",
      "XCCONFIG_DEEP_LINK_FIREBASE_HOST = #{deep_link_firebase_host()}",
      "XCCONFIG_DEEP_LINK_INSIGHTS_HOST = #{deep_link_insights_host()}",
      "XCCONFIG_CODE_PUSH_SERVER_URL = \"#{code_push_server_url()}\"",
    ]
  end

  def create_xcconfig_file(xcconfig_file_name, pods_xcconfig_relative_path)
    build_configs_directory_path = "#{root_project_directory_path()}/ios/#{PROJECT_DIRECTORY_NAME}/generated/buildConfigs"
    xcconfig_file_path = "#{build_configs_directory_path}/#{xcconfig_file_name}"
    xcconfig_file_content = xcconfig_file_content_array(pods_xcconfig_relative_path).join("\n")

    FileUtils.mkdir_p(build_configs_directory_path)
    File.open(xcconfig_file_path, 'w') { |file| file.write(xcconfig_file_content) }
  end

  desc "Creates native resource (images, icons, configs etc)"
  lane :create_native_resources do
    set_launcher_icon
    set_splash_screen
    copy_google_service_info_plist_file
    create_xcconfigs_files
  end

  desc "Add devices"
  lane :add_devices do
    devices_file = "#{root_project_directory_path()}#{DEVICES_FILE}"
    api_key = get_app_store_connect_api_key()
    register_devices(
      devices_file: devices_file,
      api_key: api_key
    )
  end

  # --- Deploy binary ---

  desc "Deploy binary"
  lane :deploy_binary do |options|
    target = options[:target]
    if target == TARGET_DEVICE
      configure_native_project(TARGET_DEVICE)
      export_method = store_to_export_method()
      create_ipa(export_method, app_store_profile_file_path())
      ipa_file = "#{root_project_directory_path()}/#{IPA_DIRECTORY}/#{IPA_NAME}"
      upload_binaries(
        binaries_path: ipa_file,
        target: target
      )
    elsif target == TARGET_SIMULATOR
      configure_native_project(TARGET_SIMULATOR)
      build_app_for_simulator
      app_dir = "#{root_project_directory_path()}/#{SIMULATOR_APP_DIRECTORY}/Build/Products/Release-iphonesimulator"

      app_file = "#{brand()}.app"
      coppiend_app_dir = brand()
      coppiend_app_file = "#{coppiend_app_dir}/#{brand()}.app"
      zipped_app_file = "#{brand()}.zip"
      Dir.chdir(app_dir) do
        sh "mkdir #{coppiend_app_dir}"
        sh "cp -r #{app_file} #{coppiend_app_file}"
        sh "zip -r #{zipped_app_file} #{coppiend_app_dir}"
      end
      upload_binaries(
        binaries_path: "#{app_dir}/#{zipped_app_file}",
        target: target
      )
    else
      raise "unknown target #{target}"
    end
  end

  def store_to_export_method()
    store = store()
    if store == STORE_STORE
      return EXPORT_METHOD_APPSTORE
    elsif store === STORE_S3
      return EXPORT_METHOD_ADHOC
    else
      raise "no EXPORT_METHOD_* for store #{store}"
    end
  end

  # ---

end
