# frozen_string_literal: true

require 'travis/cli'

module Travis
  module CLI
    class Encrypt < RepoCommand
      description 'encrypts values for the .travis.yml'
      attr_accessor :config_key

      on('-a', '--add [KEY]', 'adds it to .travis.yml under KEY (default: env.global)') do |c, value|
        c.config_key = value || 'env.global'
      end

      on('-s', '--[no-]split', 'treat each line as a separate input')
      on('-p', '--append',     "don't override existing values, instead treat as list")
      on('-x', '--override',   'override existing value')

      def run(*args)
        confirm = force_interactive.nil? || force_interactive
        error 'cannot combine --override and --append'   if append?   && override?
        error '--append without --add makes no sense'    if append?   && !add?
        error '--override without --add makes no sense'  if override? && !add?
        self.override |= !config_key.start_with?('env.') if add?      && !append?

        if args.first =~ %r{\w+/\w+} && !args.first.include?('=')
          warn 'WARNING: The name of the repository is now passed to the command with the -r option:'
          warn "    #{command("encrypt [...] -r #{args.first}")}"
          warn '  If you tried to pass the name of the repository as the first argument, you'
          warn "  probably won't get the results you wanted.\n"
        end

        data = args.join(' ')

        if data.empty?
          say color('Reading from stdin, press Ctrl+D when done', :info) if $stdin.tty?
          data = $stdin.read
        end

        data = split? ? data.split("\n") : [data.strip]
        warn_env_assignments(data)
        encrypted = data.map { |data| repository.encrypt(data) }

        if config_key
          set_config(encrypted.map { |e| { 'secure' => e } })
          confirm_and_save_travis_config confirm
        else
          list = encrypted.map { |data| format(data.inspect, '  secure: %s') }
          say(list.join("\n"), template(__FILE__), :none)
        end
      rescue OpenSSL::PKey::RSAError => e
        error "#{e.message.sub(' for key size', '')} - consider using " <<
              color('travis encrypt-file', %i[red bold]) <<
              color(' or ', :red) <<
              color('travis env set', %i[red bold])
      end

      private

      def add?
        !!config_key
      end

      def set_config(result)
        parent_config[last_key] = merge_config(result)
      end

      def merge_config(result)
        case subconfig = (parent_config[last_key] unless override?)
        when nil   then result.size == 1 ? result.first : result
        when Array then subconfig + result
        else            result.unshift(subconfig)
        end
      end

      def subconfig; end

      def key_chain
        @key_chain ||= config_key.split('.')
      end

      def last_key
        key_chain.last
      end

      def parent_config
        @parent_config ||= traverse_config(travis_config, *key_chain[0..-2])
      end

      def traverse_config(hash, key = nil, *rest)
        return hash unless key

        hash[key] = case value = hash[key]
                    when nil  then {}
                    when Hash then value
                    else { 'matrix' => Array(value) }
                    end

        traverse_config(hash[key], *rest)
      end

      def warn_env_assignments(data)
        return unless /env/.match(config_key) && data.find { |d| /=/.match(d).nil? }

        warn "Environment variables in #{config_key} should be formatted as FOO=bar"
      end
    end
  end
end

__END__
Please add the following to your <[[ color('.travis.yml', :info) ]]> file:

%s

Pro Tip: You can add it automatically by running with <[[ color('--add', :info) ]]>.
