require 'thor'

module Muchkeys
  class CLI < Thor
    include Thor::Actions

    map %w[--version -v] => :__version

    class_option :consul_url, type: :string, default: 'http://localhost:8500'

    desc "encrypt FILE", "encrypt keys from a file to put in consul"
    method_option :public_key, type: :string, required: true
    def encrypt(file)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.encrypt(file, options[:public_key])
    end

    desc "decrypt KEY", "decrypt keys from consul"
    method_option :public_key, type: :string, required: true
    method_option :private_key, type: :string, required: true
    def decrypt(consul_key)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.decrypt(consul_key, options[:public_key], options[:private_key])
    end

    desc "check", "ensure that all keys in .env in CWD are present in consul"
    def check(app_name)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.check(app_name)
    end

    desc "list", "list all keys in Application"
    def list(app_name)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.list(app_name)
    end

    desc "fetch KEY", "fetch plaintext key from consul"
    def fetch(consul_key)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.fetch(consul_key)
    end

    desc "store DATA KEY", "store data a particular key"
    method_option :public_key, type: :string
    method_option :private_key, type: :string
    def store(data, consul_key)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.store(consul_key, data, public_key: options[:public_key], private_key: options[:private_key])
    end

    desc "wipeout", "clear Consul"
    method_option :app_name, type: :string
    def wipeout
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      unless yes?("Really clear consul instance at #{Muchkeys.config.consul_url}?", Thor::Shell::Color::RED)
        say "Nothing done!"
      else
        say MuchkeysExecutor.wipeout(app_name: options[:app_name])
      end
    end

    desc "delete", "remove key"
    def delete(key)
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say MuchkeysExecutor.delete(key)
    end

    desc "--version", "Print the version"
    def __version
      Muchkeys.configure { |c| c.consul_url = options[:consul_url] }
      say Muchkeys::VERSION
    end

    module MuchkeysExecutor
      extend self

      def encrypt(file, public_key)
        string_to_encrypt = File.read(file)
        Muchkeys::ApplicationClient.new.secret_adapter.encrypt_string(string_to_encrypt.chomp, public_key)
      end

      def decrypt(consul_key, public_key, private_key)
        Muchkeys::ApplicationClient.new.fetch_key(consul_key, public_key: public_key, private_key: private_key)
      end

      def store(consul_key, data, public_key: nil, private_key: nil)
        Muchkeys.configure { |c| c.public_key = public_key; c.private_key = private_key }
        app = Muchkeys::ApplicationClient.new(Muchkeys.config)

        if data == "-"
          data = $stdin.read
        end

        app.allow_unsafe_operation do
          if public_key && private_key
            app.set_key(consul_key, data, type: :secret)
            "Secret '#{data}' stored in #{consul_key}"
          else
            app.set_key(consul_key, data)
            "'#{data}' stored in #{consul_key}"
          end
        end
      end

      def list(app_name)
        Muchkeys.configure { |c| c.application_name = app_name }
        keys = Muchkeys::ApplicationClient.new(Muchkeys.config).known_keys

        keys.join("\n")
      end

      def check(app_name)
        Muchkeys.configure { |c| c.application_name = app_name }
        Muchkeys::ApplicationClient.new(Muchkeys.config).verify_keys(*Muchkeys.env_keys)

        "All keys present and accounted for."
      end

      def wipeout(app_name: nil)
        Muchkeys.configure { |c| c.application_name = app_name }
        app = Muchkeys::ApplicationClient.new
        app.allow_unsafe_operation do
          app.each_path do |path|
            app.delete_key(path)
          end
        end

        "Everything deleted!"
      end

      def delete(consul_key)
        app = Muchkeys::ApplicationClient.new
        app.allow_unsafe_operation do
          app.delete_key(consul_key)
        end

        "Key #{consul_key} deleted"
      end

      def fetch(consul_key)
        Muchkeys::ApplicationClient.new.fetch_key(consul_key)
      end
    end
  end
end