require 'shellwords'

module Fastlane
  module Actions
    # Does a hard reset and clean on the repo
    class ResetGitRepoAction < Action
      def self.run(params)
        if params[:force] || params[:force] || Actions.lane_context[SharedValues::GIT_REPO_WAS_CLEAN_ON_START]
          paths = params[:files]

          return paths if Helper.is_test?

          if (paths || []).count == 0
            Actions.sh('git reset --hard HEAD')

            clean_options = ['q', 'f', 'd']
            clean_options << 'x' if params[:disregard_gitignore]
            clean_command = 'git clean' + ' -' + clean_options.join

            # we want to make sure that we have an array of patterns, and no nil values
            unless params[:exclude].kind_of?(Enumerable)
              params[:exclude] = [params[:exclude]].compact
            end

            # attach our exclude patterns to the command
            clean_command += ' ' + params[:exclude].map { |exclude| '-e ' + exclude.shellescape }.join(' ') unless params[:exclude].count == 0

            Actions.sh(clean_command) unless params[:skip_clean]

            Helper.log.info 'Git repo was reset and cleaned back to a pristine state.'.green
          else
            paths.each do |path|
              Helper.log.warn("Couldn't find file at path '#{path}'") unless File.exist?(path)
              Actions.sh("git checkout -- '#{path}'")
            end
            Helper.log.info "Git cleaned up #{paths.count} files.".green
          end
        else
          raise 'This is a destructive and potentially dangerous action. To protect from data loss, please add the `ensure_git_status_clean` action to the beginning of your lane, or if you\'re absolutely sure of what you\'re doing then call this action with the :force option.'.red
        end
      end

      def self.description
        "Resets git repo to a clean state by discarding uncommited changes"
      end

      def self.details
        [
          "This action will reset your git repo to a clean state, discarding any uncommitted and untracked changes. Useful in case you need to revert the repo back to a clean state, e.g. after the fastlane run.",
          "Untracked files like `.env` will also be deleted, unless `:skip_clean` is true.",
          "It's a pretty drastic action so it comes with a sort of safety latch. It will only proceed with the reset if either of these conditions are met:",
          "You have called the ensure_git_status_clean action prior to calling this action. This ensures that your repo started off in a clean state, so the only things that will get destroyed by this action are files that are created as a byproduct of the fastlane run."
        ].join(' ')
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :files,
                                       env_name: "FL_RESET_GIT_FILES",
                                       description: "Array of files the changes should be discarded. If not given, all files will be discarded",
                                       optional: true,
                                       is_string: false,
                                       verify_block: proc do |value|
                                         raise "Please pass an array only" unless value.kind_of? Array
                                       end),
          FastlaneCore::ConfigItem.new(key: :force,
                                       env_name: "FL_RESET_GIT_FORCE",
                                       description: "Skip verifying of previously clean state of repo. Only recommended in combination with `files` option",
                                       is_string: false,
                                       default_value: false),
          FastlaneCore::ConfigItem.new(key: :skip_clean,
                                       env_name: "FL_RESET_GIT_SKIP_CLEAN",
                                       description: "Skip 'git clean' to avoid removing untracked files like `.env`. Defaults to false",
                                       is_string: false,
                                       default_value: false),
          FastlaneCore::ConfigItem.new(key: :disregard_gitignore,
                                       env_name: "FL_RESET_GIT_DISREGARD_GITIGNORE",
                                       description: "Setting this to true will clean the whole repository, ignoring anything in your local .gitignore. Set this to true if you want the equivalent of a fresh clone, and for all untracked and ignore files to also be removed",
                                       is_string: false,
                                       optional: true,
                                       default_value: true),
          FastlaneCore::ConfigItem.new(key: :exclude,
                                       env_name: "FL_RESET_GIT_EXCLUDE",
                                       description: "You can pass a string, or array of, file pattern(s) here which you want to have survive the cleaning process, and remain on disk. E.g. to leave the `artifacts` directory you would specify `exclude: 'artifacts'`. Make sure this pattern is also in your gitignore! See the gitignore documentation for info on patterns",
                                       is_string: false,
                                       optional: true)
        ]
      end

      def self.author
        'lmirosevic'
      end

      def self.is_supported?(platform)
        true
      end
    end
  end
end