require "imap/backup/setup/account/header"
require "imap/backup/setup/asker"
require "imap/backup/setup/backup_path"
require "imap/backup/setup/connection_tester"
require "imap/backup/setup/email_changer"
require "imap/backup/setup/folder_chooser"

module Imap; end

module Imap::Backup
  class Setup; end

  # Handles interactive account setup
  class Setup::Account
    # @param config [Configuration] the application configuration
    # @param account [Account] an Account
    # @param highline [Higline] the configured Highline instance
    def initialize(config, account, highline)
      @account = account
      @config = config
      @highline = highline
    end

    # Shows the menu
    # @return [void]
    def run
      if !account.local_path
        account.local_path = File.join(config.path, account.username.tr("@", "_"))
      end

      catch :done do
        loop do
          Kernel.system("clear")
          create_menu
        end
      end
    end

    private

    attr_reader :account
    attr_reader :config
    attr_reader :highline

    def create_menu
      highline.choose do |menu|
        header menu
        modify_email menu
        modify_password menu
        modify_server menu
        modify_connection_options menu
        test_connection menu
        toggle_mirror_mode menu
        modify_backup_path menu
        toggle_folder_blacklist menu
        choose_folders menu
        modify_multi_fetch_size menu
        toggle_reset_seen_flags_after_fetch menu
        rotate_status menu
        delete_account menu
        menu.choice("(q) return to main menu") { throw :done }
        menu.hidden("quit") { throw :done }
      end
    end

    def header(menu)
      Setup::Account::Header.new(menu: menu, account: account).run
    end

    def modify_email(menu)
      menu.choice("modify email") do
        Setup::EmailChanger.new(account: account, config: config).run
      end
    end

    def modify_password(menu)
      menu.choice("modify password") do
        password = Setup::Asker.password

        account.password = password if !password.nil?
      end
    end

    def modify_backup_path(menu)
      menu.choice("modify backup path") do
        Setup::BackupPath.new(account: account, config: config).run
      end
    end

    def toggle_folder_blacklist(menu)
      menu_item = "toggle folder inclusion mode (whitelist/blacklist)"
      new_value = account.folder_blacklist ? nil : true
      menu.choice(menu_item) do
        account.folder_blacklist = new_value
      end
    end

    def choose_folders(menu)
      action = account.folder_blacklist ? "exclude from backups" : "include in backups"
      menu.choice("choose folders to #{action}") do
        Setup::FolderChooser.new(account).run
      end
    end

    def toggle_mirror_mode(menu)
      menu_item = "toggle mode (keep/mirror)"
      new_value = account.mirror_mode ? nil : true
      menu.choice(menu_item) do
        account.mirror_mode = new_value
      end
    end

    def modify_multi_fetch_size(menu)
      menu.choice("modify multi-fetch size (number of emails to fetch at a time)") do
        size = highline.ask("size: ")
        int = size.to_i
        account.multi_fetch_size = int if int.positive?
      end
    end

    def modify_server(menu)
      menu.choice("modify server") do
        server = highline.ask("server: ")
        account.server = server if !server.nil?
      end
    end

    def modify_connection_options(menu)
      menu.choice("modify connection options") do
        connection_options = highline.ask("connections options (as JSON): ")
        if !connection_options.nil?
          begin
            account.connection_options = connection_options
          rescue JSON::ParserError
            Kernel.puts "Malformed JSON, please try again"
            highline.ask "Press a key "
          end
        end
      end
    end

    def toggle_reset_seen_flags_after_fetch(menu)
      menu_item =
        if account.reset_seen_flags_after_fetch
          "don't fix changes to unread flags during download"
        else
          "fix changes to unread flags during download"
        end
      new_value = account.reset_seen_flags_after_fetch ? nil : true
      menu.choice(menu_item) do
        account.reset_seen_flags_after_fetch = new_value
      end
    end

    def rotate_status(menu)
      current_status = account.status
      statuses = %w[active archived offline]
      current_index = statuses.index(current_status) || 0
      next_index = (current_index + 1) % statuses.length
      next_status = statuses[next_index]

      menu_item = "change status (currently: #{current_status} -> #{next_status})"
      menu.choice(menu_item) do
        account.status = next_status
      end
    end

    def test_connection(menu)
      menu.choice("test connection") do
        result = Setup::ConnectionTester.new(account).test
        Kernel.puts result
        highline.ask "Press a key "
      end
    end

    def delete_account(menu)
      menu.choice("delete") do
        if highline.agree("Are you sure? (y/n) ")
          account.mark_for_deletion
          throw :done
        end
      end
    end
  end
end
