require 'dev/ui'
require 'readline'

module Dev
  module UI
    module Prompt
      class << self
        def ask(question, options: nil, default: nil, is_file: nil, allow_empty: true)
          if (default && !allow_empty) || (options && (default || is_file))
            raise(ArgumentError, 'conflicting arguments')
          end

          if default
            puts_question("#{question} (empty = #{default})")
          elsif options
            puts_question("#{question} {{yellow:(choose with ↑ ↓ ⏎)}}")
          else
            puts_question(question)
          end

          return InteractivePrompt.call(options) if options

          loop do
            line = readline(is_file: is_file)

            if line.empty? && default
              write_default_over_empty_input(default)
              return default
            end

            if !line.empty? || allow_empty
              return line
            end
          end
        end

        def confirm(question)
          puts_question("#{question} {{yellow:(choose with ↑ ↓ ⏎)}}")
          InteractivePrompt.call(%w(yes no)) == 'yes'
        end

        private

        def write_default_over_empty_input(default)
          Dev::UI.raw do
            STDERR.puts(
              Dev::UI::ANSI.cursor_up(1) +
              "\r" +
              Dev::UI::ANSI.cursor_forward(4) + # TODO: width
              default +
              Dev::UI::Color::RESET.code
            )
          end
        end

        def puts_question(str)
          Dev::UI.with_frame_color(:blue) do
            STDOUT.puts(Dev::UI.fmt('{{?}} ' + str))
          end
        end

        def readline(is_file: false)
          if is_file
            Readline.completion_proc = Readline::FILENAME_COMPLETION_PROC
            Readline.completion_append_character = ""
          else
            Readline.completion_proc = proc { |*| nil }
            Readline.completion_append_character = " "
          end

          # because Readline is a C library, Dev::UI's hooks into $stdout don't
          # work. We could work around this by having Dev::UI use a pipe and a
          # thread to manage output, but the current strategy feels like a
          # better tradeoff.
          prefix = Dev::UI.with_frame_color(:blue) { Dev::UI::Frame.prefix }
          prompt = prefix + Dev::UI.fmt('{{blue:> }}{{yellow:')

          begin
            Readline.readline(prompt, true).chomp
          rescue Interrupt
            Dev::UI.raw { STDERR.puts('^C' + Dev::UI::Color::RESET.code) }
            raise
          end
        end
      end
    end
  end
end