require_relative '../client'

module NaviClient

  ##
  # This class represents the client for local lockbox mode.
  # In local-lockbox mode, all the content will be saved and also being used from the local file system
  #
  class Local
    include Client
    CONFIG_PATH = File.join(ENV['HOME'], '/.navi/config.yml')

    def initialize(sso_web_url = 'http://localhost:3008', client_type = "local", gen_csv_url = "http://localhost:3020/v2/generate_csv_input")
      # flag to print Ruby library debug info (very detailed)
      @net_imap_debug = false

      # flag to mark email as read after gets downloaded.
      @mark_as_read = false

      # flag to turn on/off debug mode.
      @debug = false

      # override the log file
      mkdir_if_not_exist(config['client_log_file'])
      @logger = Logger.new(config['client_log_file'])

      # sso_web (authentication) config.
      @sso_web_url = sso_web_url
      # authentication token received from sso_web used to authenticate the request to database_api
      @token = nil

      @download_path = config['download_path'] + config['username'] + '/'

      # client_type
      @client_type = client_type
      @gen_csv_url = gen_csv_url
    end

    #
    # login
    #
    # login to the navi-cloud and get the authentication token
    #
    def login
      url = "#{@sso_web_url}/oauth/token"
      provider_url = url
      token = HTTParty.post(provider_url,
                             body: {
                               client_id: config["uid"], # get from sso_web application
                               client_secret: config["secret_key"],
                               grant_type: "client_credentials"
                             }
                            )['access_token']
      @token = "Token: #{token}##{config['username']}"
    end

    ##
    # Downloads the email content from imap-server and save it to the download_path
    #
    def download(message, custom_uid)
      download_path = config['download_path'] + config['username'] + '/'
      if ['text/plain', 'text/html'].include? message.mime_type

        h = Hash.new
        out_file = download_path + message.mime_type + "/"+custom_uid
        mkdir_if_not_exist(out_file)

        File.open(out_file, 'w') { |file| file.write(encrypt(message.decoded)) }
        key = message.mime_type.split("/").join("_")

        h[key] = out_file
        return h
      end
    end

    ##
    # save data to download_path with file named by filename params.
    # Input is hashmap, and it save the hashmap as yml format.
    #
    def save(data={}, filename)
      download_path = config['download_path'] + config['username'] + '/'
      filepath = download_path + filename + ".yml"

      mkdir_if_not_exist(filepath)

      File.write(filepath, data.to_yaml)
      return filepath
    end

    ##
    # send bulk request to navi-ai service with list of input files downloaded from imap server.
    #
    def send_request(in_filenames = [])
      unless in_filenames.empty?
        download_path = config['download_path']
        filename = download_path + config['username'] + "/inputs/" + (Time.now.to_f * 1000).to_s

        mkdir_if_not_exist(filename)

        File.open(filename, 'w') do |f|
          in_filenames.each { |element| f.puts(element) }
        end

        HTTPService::NaviAI.start(filepath: filename, client_type: @client_type, token: @token, email: config['username'])
      end
    end

    def mkdir_if_not_exist(filepath)
      dirname = File.dirname(filepath)
      unless File.directory?(dirname)
        FileUtils.mkdir_p(dirname)
      end
    end

    #
    # idle_loop
    #
    # check for any further mail with "real-time" responsiveness.
    # retrieve any mail from a folder, following specified search condition
    # for any mail retrieved call a specified block
    #
    def idle_loop(imap, search_condition, folder, server, username, password)

      @logger.info "\nwaiting new mails (IDLE loop)..."

      loop do
        begin
          imap.select folder
          imap.idle do |resp|

            # You'll get all the things from the server. For new emails (EXISTS)
            if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS"

              @logger.debug resp.inspect if @debug
              # Got something. Send DONE. This breaks you out of the blocking call
              imap.idle_done
            end
          end

          # We're out, which means there are some emails ready for us.
          # Go do a search for UNSEEN and fetch them.
          filenames = []
          retrieve_emails(imap, search_condition, folder) { |mail, i, isLast, id| filenames << process_email(mail, id)}
          self.send_request(filenames)

          HTTPService::NaviAI.generate_csv("#{@gen_csv_url}?email=#{username}", @token)

          @logger.debug "Process Completed." if @debug

        rescue SignalException => e
          # http://stackoverflow.com/questions/2089421/capturing-ctrl-c-in-ruby
          @logger.info "Signal received at #{time_now}: #{e.class}. #{e.message}"
          shutdown imap

        rescue Net::IMAP::Error => e
          @logger.error "Net::IMAP::Error at #{time_now}: #{e.class}. #{e.message}"

          # timeout ? reopen connection
          imap = imap_connection(server, username, password) #if e.message == 'connection closed'
          @logger.info "reconnected to server: #{server}"

        rescue Exception => e
          @logger.error "Something went wrong at #{time_now}: #{e.class}. #{e.message}"

          imap = imap_connection(server, username, password)
          @logger.info "reconnected to server: #{server}"
        end
      end
    end

    def initate_csv_generation(username)
      HTTPService::NaviAI.generate_csv("#{@gen_csv_url}?email=#{username}", @token)
    end

    def config
      YAML.load_file(CONFIG_PATH)
    end

    def save_aes_key_iv(key, iv)
      aes_key_path = File.join(ENV['HOME'] + '/.navi/.aes.key')
      iv_path = File.join(ENV['HOME'] + '/.navi/.aes.iv')

      File.open(aes_key_path, "w") { |f| f.write(Base64.encode64(key)) }
      File.open(iv_path, "w") { |f| f.write(Base64.encode64(iv)) }
      return aes_key_path, iv_path
    end

    def update_config(data)
      File.write(CONFIG_PATH, YAML.dump(data))
    end
  end
end