module PandaPal
  module ConsoleHelpers
    extend self

    COLORS = {
      "black" => 0,
      "red" => 1,
      "green" => 2,
      "yellow" => 3,
      "blue" => 4,
      "purple" => 5,
      "magenta" => 5,
      "cyan" => 6,
      "white" => 7
    }.freeze

    COLORS.each_pair do |color, value|
      define_method color do |text|
        "\033[0;#{30 + value}m#{text}\033[0m"
      end

      define_method "bright_#{color}" do |text|
        "\033[1;#{30 + value}m#{text}\033[0m"
      end
    end

    def pandapalrc
      # TODO Consider searching app and parent dirs before ~/
      @pandapalrc ||= YAML.load(File.read(File.expand_path("~/pandapalrc.yml"))) rescue {}
    end

    def prompt(prompt = "", default: nil)
      prompt = prompt + " (#{default})" if default.present?
      puts prompt
      print "> "
      v = gets.chomp.downcase
      return default if v == ""
      v
    end

    def prompt_options(options, prompt = "", default: nil)
      options = options.map(&:to_s)
      prompt = prompt + " (" + options.map { |o| o == default ? o.capitalize : o }.join("/") + ")"
      i = 0
      loop do
        puts prompt
        print "> "
        i += 1
        v = gets.chomp.downcase
        return v if options.include?(v)
        return default if v == ""
        return nil if i > 3
        puts "Invalid Input."
      end
    end

    def prompt_yes_no(prompt = "", default: true)
      result = prompt_options(["y", "n"], prompt, default: default ? "y" : "n")
      return true if result == "y"
      return false if result == "n"
      result
    end

    def prompt_pry_retry(prompt = "Retry?", default: false)
      default = default ? "y" : "n" unless default.is_a?(String)
      result = prompt_options(["y", "n", "pry"], prompt, default: default ? "y" : "n")
      if result == "pry"
        binding.pry
        return true
      end
      return true if result == "y"
      return false if result == "n"
      result
    end

    def open_editor(file_path)
      raise "EDITOR environment variable not set" unless ENV["EDITOR"].present?

      args = Shellwords.shellwords(ENV['EDITOR'])
      args << file_path
      Kernel::system(*args)
    end

    def open_string_editor(string, file: nil, name: nil, require_save: true)
      file_obj = file.present? ? File.new(file) : Tempfile.new([File.basename(name, File.extname(name)), File.extname(name)])
      File.open(file_obj.path, 'w') { |f| f.write(string) }

      mtime = File.stat(file_obj.path).mtime

      path = file_obj.path
      file_obj.close rescue nil
      open_editor(path)

      return :aborted unless !require_save || mtime < File.stat(file_obj.path).mtime

      File.read(path)
    end

    class CodeBuilder
      def initialize(indent: 0)
        @code = ""
        @line_prefix = ["  "] * indent
      end

      def <<(line)
        if line.is_a?(Array)
          line.each do |l|
            self << l
          end
        else
          bits = line.split("\n", -1)

          push_bit(bits.shift)

          bits.each do |bit|
            @code << "\n"
            push_bit(bit)
          end
        end
      end

      def ensure_line
        return if @code.ends_with?("\n")
        @code << "\n"
      end

      def block(char = nil)
        indent!(char)
        yield
      ensure
        dedent!
      end

      def indent!(char = nil)
        @line_prefix << (char || "  ")
      end

      def dedent!
        @line_prefix.pop
      end

      def to_s
        @code
      end

      protected

      def push_bit(bit)
        return unless bit.present?

        @code << @line_prefix.join if @code.ends_with?("\n")
        @code << bit
      end
    end
  end
end