require 'hiera/backend/eyaml/options'
require 'hiera/backend/eyaml/parser/parser'
require 'hiera/backend/eyaml/parser/encrypted_tokens'
require 'hiera/backend/eyaml/subcommand'

class Hiera
  module Backend
    module Eyaml
      module Subcommands
        class Encrypt < Subcommand
          def self.options
            [{ name: :password,
               description: 'Source input is a password entered on the terminal',
               short: 'p', },
             { name: :string,
               description: 'Source input is a string provided as an argument',
               short: 's',
               type: :string, },
             { name: :file,
               description: 'Source input is a regular file',
               short: 'f',
               type: :string, },
             { name: :stdin,
               description: 'Source input is taken from stdin',
               short: :none, },
             { name: :eyaml,
               description: 'Source input is an eyaml file',
               short: 'e',
               type: :string, },
             { name: :output,
               description: 'Output format of final result (examples, block, string)',
               type: :string,
               short: 'o',
               default: 'examples', },
             { name: :label,
               description: 'Apply a label to the encrypted result',
               short: 'l',
               type: :string, },]
          end

          def self.description
            'encrypt some data'
          end

          def self.validate(options)
            sources = %i[password string file stdin eyaml].collect { |x| x if options[x] }.compact
            Optimist.die 'You must specify a source' if sources.count.zero?
            Optimist.die "You can only specify one of (#{sources.join(', ')})" if sources.count > 1
            options[:source] = sources.first

            options[:input_data] = case options[:source]
                                   when :password
                                     require 'hiera/backend/eyaml/highlinehelper'
                                     HighlineHelper.read_password
                                   when :string
                                     options[:string]
                                   when :file
                                     File.read options[:file]
                                   when :stdin
                                     STDIN.read
                                   when :eyaml
                                     File.read options[:eyaml]
                                   end
            options
          end

          def self.execute
            case Eyaml::Options[:source]
            when :eyaml
              parser = Parser::ParserFactory.decrypted_parser
              tokens = parser.parse(Eyaml::Options[:input_data])
              encrypted = tokens.map { |token| token.to_encrypted }
              encrypted.join
            else
              encryptor = Encryptor.find
              ciphertext = encryptor.encode(encryptor.encrypt(Eyaml::Options[:input_data]))
              token = Parser::EncToken.new(:block, Eyaml::Options[:input_data], encryptor, ciphertext, nil, '  ')
              case Eyaml::Options[:output]
              when 'block'
                token.to_encrypted label: Eyaml::Options[:label], use_chevron: !Eyaml::Options[:label].nil?,
                                   format: :block
              when 'string'
                token.to_encrypted label: Eyaml::Options[:label], format: :string
              when 'examples'
                string = token.to_encrypted label: Eyaml::Options[:label] || 'string', format: :string
                block = token.to_encrypted label: Eyaml::Options[:label] || 'block', format: :block
                "#{string}\n\nOR\n\n#{block}"
              else
                token.to_encrypted format: :string
              end
            end
          end

          def self.print_out(string)
            case Eyaml::Options[:output]
            when 'string'
              # Do not include a newline, so that 'eyaml decrypt' of the
              # output returns the original input.
              print string
            else
              # The output is a text file, so ensure there is a final newline.
              puts string
            end
          end
        end
      end
    end
  end
end
