module Fastlane
  class SetupIos < Setup
    # the tools that are already enabled
    attr_accessor :tools
    attr_accessor :project
    attr_accessor :apple_id

    attr_accessor :portal_ref
    attr_accessor :itc_ref

    attr_accessor :dev_portal_team
    attr_accessor :itc_team

    attr_accessor :app_identifier
    attr_accessor :app_name

    def run
      show_infos

      FastlaneCore::FastlaneFolder.create_folder! unless Helper.is_test?
      is_manual_setup = false

      begin
        setup_project
        ask_for_apple_id
        detect_if_app_is_available
        print_config_table
        if UI.confirm("Please confirm the above values")
          default_setup
        else
          is_manual_setup = true
          manual_setup
        end
        UI.success('Successfully finished setting up fastlane')
      rescue => ex # this will also be caused by Ctrl + C
        if is_manual_setup
          handle_exception(exception: ex)
        else
          UI.error(ex.to_s)
          UI.error('An error occured during the setup process. Falling back to manual setup!')
          try_manual_setup
        end
      end
      # rubocop:enable Lint/RescueException
    end

    def handle_exception(exception: nil)
      # Something went wrong with the setup, clear the folder again
      # and restore previous files
      UI.error('Error occurred with the setup program! Reverting changes now!')
      restore_previous_state
      raise exception
    end

    def try_manual_setup
      manual_setup
    rescue => ex
      handle_exception(exception: ex)
    end

    def default_setup
      copy_existing_files
      generate_appfile(manually: false)
      detect_installed_tools # after copying the existing files
      if self.itc_ref.nil? && self.portal_ref.nil?
        create_app_if_necessary
      end
      enable_deliver
      generate_fastfile(manually: false)
      show_analytics
    end

    def manual_setup
      copy_existing_files
      generate_appfile(manually: true)
      detect_installed_tools # after copying the existing files
      ask_to_enable_other_tools
      generate_fastfile(manually: true)
      show_analytics
    end

    def ask_to_enable_other_tools
      if self.itc_ref.nil? && self.portal_ref.nil?
        wants_to_create_app = agree('Would you like to create your app on iTunes Connect and the Developer Portal? (y/n)', true)
        if wants_to_create_app
          create_app_if_necessary
          detect_if_app_is_available # check if the app was, in fact, created.
        end
      end
      if self.itc_ref && self.portal_ref
        wants_to_setup_deliver = agree("Do you want to setup 'deliver', which is used to upload app screenshots, app metadata and app updates to the App Store? This requires the app to be in the App Store already. (y/n)".yellow, true)
        enable_deliver if wants_to_setup_deliver
      end
    end

    def setup_project
      config = {}
      FastlaneCore::Project.detect_projects(config)
      self.project = FastlaneCore::Project.new(config)
      self.app_identifier = self.project.default_app_identifier # These two vars need to be accessed in order to be set
      self.app_name = self.project.default_app_name # They are set as a side effect, this could/should be changed down the road
    end

    def print_config_table
      rows = []
      rows << ["Apple ID", self.apple_id]
      rows << ["App Name", self.app_name]
      rows << ["App Identifier", self.app_identifier]
      rows << [(self.project.is_workspace ? "Workspace" : "Project"), self.project.path]
      require 'terminal-table'
      puts ""
      puts Terminal::Table.new(rows: rows, title: "Detected Values")
      puts ""

      unless self.itc_ref
        UI.important "This app identifier doesn't exist on iTunes Connect yet, it will be created for you"
      end

      unless self.portal_ref
        UI.important "This app identifier doesn't exist on the Apple Developer Portal yet, it will be created for you"
      end
    end

    def show_infos
      UI.success('This setup will help you get up and running in no time.')
      UI.success("fastlane will check what tools you're already using and set up")
      UI.success('the tool automatically for you. Have fun! ')
    end

    def files_to_copy
      ['Deliverfile', 'deliver', 'screenshots', 'metadata']
    end

    def copy_existing_files
      files_to_copy.each do |current|
        current = File.join(File.expand_path('..', FastlaneCore::FastlaneFolder.path), current)
        next unless File.exist?(current)
        file_name = File.basename(current)
        to_path = File.join(folder, file_name)
        UI.success("Moving '#{current}' to '#{to_path}'")
        FileUtils.mv(current, to_path)
      end
    end

    def ask_for_apple_id
      self.apple_id ||= UI.input("Your Apple ID (e.g. fastlane@krausefx.com): ")
    end

    def ask_for_app_identifier
      self.app_identifier = UI.input("App Identifier (com.krausefx.app): ")
    end

    def generate_appfile(manually: false)
      template = File.read("#{Fastlane::ROOT}/lib/assets/AppfileTemplate")
      if manually
        ask_for_app_identifier
        ask_for_apple_id
      end

      template.gsub!('[[DEV_PORTAL_TEAM_ID]]', self.dev_portal_team) if self.dev_portal_team

      itc_team = self.itc_team ? "itc_team_id \"#{self.itc_team}\" # iTunes Connect Team ID\n" : ""
      template.gsub!('[[ITC_TEAM]]', itc_team)

      template.gsub!('[[APP_IDENTIFIER]]', self.app_identifier)
      template.gsub!('[[APPLE_ID]]', self.apple_id)

      path = File.join(folder, 'Appfile')
      File.write(path, template)
      UI.success("Created new file '#{path}'. Edit it to manage your preferred app metadata information.")
    end

    # Detect if the app was created on the Dev Portal / iTC
    def detect_if_app_is_available
      require 'spaceship'

      UI.important "Verifying if app is available on the Apple Developer Portal and iTunes Connect..."
      UI.message "Starting login with user '#{self.apple_id}'"
      Spaceship.login(self.apple_id, nil)
      self.dev_portal_team = Spaceship.select_team
      self.portal_ref = Spaceship::App.find(self.app_identifier)

      Spaceship::Tunes.login(@apple_id, nil)
      self.itc_team = Spaceship::Tunes.select_team
      self.itc_ref = Spaceship::Application.find(self.app_identifier)
    end

    def create_app_if_necessary
      UI.important "Creating the app on iTunes Connect and the Apple Developer Portal"
      require 'produce'
      config = {} # this has to be done like this
      FastlaneCore::Project.detect_projects(config)
      project = FastlaneCore::Project.new(config)

      produce_options_hash = {
        app_name: project.app_name,
        app_identifier: self.app_identifier
      }
      Produce.config = FastlaneCore::Configuration.create(Produce::Options.available_options, produce_options_hash)
      begin
        ENV['PRODUCE_APPLE_ID'] = Produce::Manager.start_producing
      rescue => exception
        if exception.to_s.include?("The App Name you entered has already been used")
          UI.important("It looks like that #{project.app_name} has already been taken by someone else, please enter an alternative.")
          Produce.config[:app_name] = UI.input("App Name: ")
          Produce.config[:skip_devcenter] = true # since we failed on iTC
          ENV['PRODUCE_APPLE_ID'] = Produce::Manager.start_producing
        end
      end
    end

    def detect_installed_tools
      self.tools = {}
      self.tools[:snapshot] = File.exist?(File.join(folder, 'Snapfile'))
      self.tools[:cocoapods] = File.exist?(File.join(File.expand_path('..', folder), 'Podfile'))
      self.tools[:carthage] = File.exist?(File.join(File.expand_path('..', folder), 'Cartfile'))
    end

    def enable_deliver
      UI.message("Loading up 'deliver', this might take a few seconds")
      require 'deliver'
      require 'deliver/setup'
      options = FastlaneCore::Configuration.create(Deliver::Options.available_options, {})
      Deliver::Runner.new(options) # to login...
      Deliver::Setup.new.run(options)
    end

    def generate_fastfile(manually: false)
      scheme = self.project.schemes.first unless manually

      template = File.read("#{Fastlane::ROOT}/lib/assets/DefaultFastfileTemplate")

      scheme = UI.input("Optional: The scheme name of your app (If you don't need one, just hit Enter): ") unless scheme
      if scheme.length > 0
        template.gsub!('[[SCHEME]]', "(scheme: \"#{scheme}\")")
      else
        template.gsub!('[[SCHEME]]', "")
      end

      template.gsub!('snapshot', '# snapshot') unless self.tools[:snapshot]
      template.gsub!('cocoapods', '') unless self.tools[:cocoapods]
      template.gsub!('carthage', '') unless self.tools[:carthage]
      template.gsub!('[[FASTLANE_VERSION]]', Fastlane::VERSION)

      self.tools.each do |key, value|
        UI.message("'#{key}' enabled.".magenta) if value
        UI.important("'#{key}' not enabled.") unless value
      end

      path = File.join(folder, 'Fastfile')
      File.write(path, template)
      UI.success("Created new file '#{path}'. Edit it to manage your own deployment lanes.")
    end

    def folder
      FastlaneCore::FastlaneFolder.path
    end

    def restore_previous_state
      # Move all moved files back
      files_to_copy.each do |current|
        from_path = File.join(folder, current)
        to_path = File.basename(current)
        if File.exist?(from_path)
          UI.important("Moving '#{from_path}' to '#{to_path}'")
          FileUtils.mv(from_path, to_path)
        end
      end

      UI.important("Deleting the 'fastlane' folder")
      FileUtils.rm_rf(folder)
    end
  end
end