# frozen_string_literal: true

module AnimePush
  module Command
    class EditCommand < AnimePush::Command::BaseCommand

      # Start command.
      def start
        set_maps

        @anime = select_anime
        logger.debug(@anime)

        loop do
          response = edit
          case response
          when :save
            puts 'Save anime.'
            logger.debug(@anime)
            break
          when :cancel
            puts 'Cancel edit command.'
            return
          else
            puts ''
            next
          end
        end

        save_anime
      end

      private

      # Set method maps
      def set_maps
        @methods_map = {
          title: :edit_title,
          week: :edit_week,
          hour: :edit_hour,
          min: :edit_min,
          channel: :edit_channel,
          url: :edit_url,
          save: :run_save_anime,
          cancel: :run_cancel,
        }
        @ignore_values = [:save, :cancel]
      end

      # Select edit target anime.
      #
      # @return [AnimePush::Anime]
      def select_anime
        id = options[:id]
        if id.is_a?(Integer)
          anime = AnimePush::Anime.find(id)
          return anime unless anime.nil?
        end

        cli.choose do |menu|
          menu.prompt = '編集するアニメのIDを入力。'
          AnimePush::Anime.all.each do |selected_anime|
            menu.choice(selected_anime.title) { selected_anime }
          end
        end
      end

      # Save anime.
      def save_anime
        if @anime.valid?
          @anime.save

          puts '保存完了。'
          return true
        end

        puts 'Anime data has errors.'
        logger.error(@anime.errors)
        logger.error(@anime.errors.messages)
        @anime.errors.messages.each do |name, messages|
          messages.each do |message|
            puts "#{name} : #{message}"
          end
        end

        false
      end

      # Edit
      def edit
        method = cli.choose do |menu|
          menu.prompt = '編集する項目を選択。'
          @methods_map.each do |value, method_name|
            name = if @ignore_values.include?(value)
                     value.to_s
                   else
                     current = @anime.send(value) || 'null'
                     "#{value} (current value : #{current})"
                   end
            menu.choice(name) { method_name }
          end
        end

        call_method(method)
      end

      # Call method
      #
      # @param [Symbol] method_name
      # @return [Object]
      def call_method(method_name)
        logger.debug("Call #{method_name}")
        send(method_name)
      end

      # Edit title.
      def edit_title
        title = cli.ask('タイトルを入力してください。', String) do |q|
          q.validate = /\S+/
        end
        @anime.title = title
      end

      # Edit week
      def edit_week
        week = cli.ask('曜日を0~6で入力してください。(0 : 日, 6:土)', Integer) do |q|
          q.in = 0..6
        end
        @anime.week = week
      end

      # Edit hour.
      def edit_hour
        hour = cli.ask('時間を0~23で入力してください。', Integer) do |q|
          q.in = 0..23
        end
        @anime.hour = hour
      end

      # Edit min.
      def edit_min
        min = cli.ask('分を0~59で入力してください。', Integer) do |q|
          q.in = 0..59
        end
        @anime.min = min
      end

      # Edit channel.
      def edit_channel
        channel = cli.ask('チャンネルを入力してください。') do |q|
          q.validate = /\S+/
        end
        @anime.channel = channel
      end

      # Edit url.
      def edit_url
        url = cli.ask('URLを入力してください。設定しない場合は空のままEnter。')
        @anime.url = url.blank? ? nil : url
      end

      # @return [Symbol]
      def run_save_anime
        :save
      end

      # @return [Symbol]
      def run_cancel
        :cancel
      end
    end
  end
end
