require_relative '../client'
require_relative '../gmail_client'

module NaviEmailSync

  ##
  # 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

    CONFIG_PATH = File.join(ENV['HOME'], '/.navi/config.yml')

    def initialize(method, client_type = "local", debug = true)
      # 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 = debug

      # 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 = ENV['api_url']
      # authentication token received from sso_web used to authenticate the request to database_api
      @token = nil

      @download_path = config['download_path'] + config['identifier'] + "/#{method}/"
      @current_user_email = config['username']

      # client_type
      @client_type = client_type
      @gen_csv_url = "#{ENV['api_url']}/v2/generate_csv_input"
      @env = ENV['env']?ENV['env']:Rails.env
    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

    def override_logger(logger)
      @logger = logger
    end

    def set_token(token)
      @token = token
    end

    ##
    # Downloads the email content from imap-server and save it to the download_path
    #
    def download(message, custom_uid)

      mime_type = message["mime_type"].nil? ? message.mime_type : message["mime_type"]

      if ['text/plain', 'text/html', 'text/alternative'].include? mime_type

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

        File.open(out_file, 'w') { |file| file.write(encrypt(message["decoded"].nil? ? message.decoded : message["decoded"])) }
        key = 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, stamp)
      temp_path = @download_path + "inputs/temp-#{stamp}"
      filepath = @download_path + filename + ".yml"

      mkdir_if_not_exist(filepath)
      mkdir_if_not_exist(temp_path)
      File.open(filepath, 'w') do |f|
        f.puts(data.to_yaml)
      end

      File.open(temp_path, 'a') do |f|
        f.puts(filepath)
      end

      return filepath
    end

    ##
    # send bulk request to navi-ai service with list of input files downloaded from imap server.
    #
    def send_request(in_filenames = [], stamp, is_last: false, email_count:)
      unless in_filenames.empty?
        filename = @download_path + "inputs/#{stamp}"

        mkdir_if_not_exist(filename)

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

        response = HTTPService::NaviAI.start(filepath: filename, client_type: @client_type, token: @token, email: config['username'], is_last: is_last, email_count: email_count, total_emails: @total_emails)
        return response
      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(search_condition, folder, server, username, password, callback = nil, navi_control = nil)

      @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
              @logger.debug "New mail received" if @debug
              @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 = []
          stamp = (Time.now.to_f * 1000).to_s
          init_messages folder, search_condition

          fetch_emails() { |mail, i, isLast, id|
            callback.call(mail, i, id, false) unless callback.nil?
            filenames << process_email(mail, id, stamp)
            callback.call(mail, i, id, true) unless callback.nil?
          }

          @logger.info "Sending Request for #{filenames.length} emails to Navi AI."
          navi_control.call(true) unless navi_control.nil?
          self.send_request(filenames, stamp, is_last: true, email_count: filenames.length)

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

          @logger.debug "Process Completed." if @debug
          navi_control.call(false) unless navi_control.nil?

        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}"
          @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 getMessageUUIds(path)
      File.directory?(path) ? (Dir.entries path).map { |i| i.split("_").first } : []
    end

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

    def parse_prev_failed_emails
      changes = false
      Dir[@download_path + 'inputs/temp*'].each do |file_name|
        if File.file? file_name
          HTTPService::NaviAI.start(filepath: file_name, client_type: @client_type, token: @token, email: @current_user_email, total_emails: @total_emails)
          changes = true
        end
      end
      return changes
    end

  end

  class GmailApi < Local
    include GmailClient
  end

  class ImapApi < Local
    include Client
  end
end