module VMDB
  module Util
    def self.http_proxy(proxy_config = :default)
      proxy = ::Settings.http_proxy[proxy_config].to_hash
      proxy = ::Settings.http_proxy.to_hash unless proxy[:host]
      return nil if proxy[:host].blank?

      proxy
    end

    def self.http_proxy_uri(proxy_config = :default)
      proxy = http_proxy(proxy_config)
      return if proxy.nil?

      user       = proxy.delete(:user)
      user     &&= CGI.escape(user)
      password   = proxy.delete(:password)
      password &&= CGI.escape(password)
      userinfo   = "#{user}:#{password}".chomp(":") if user.present?

      proxy[:userinfo] = userinfo
      proxy[:scheme] ||= "http"
      proxy[:port]   &&= proxy[:port].to_i

      URI::Generic.build(proxy)
    end

    def self.compressed_log_patterns
      # From a log file create an array of strings containing the date patterns
      log_dir = Rails.root.join("log").to_s
      gz_pattern = File.join(log_dir, "*[0-9][0-9].gz")
      Dir.glob(gz_pattern).each_with_object([]) do |f, arr|
        f =~ /.+-(\d+\.gz)/
        name = File.join(log_dir, "*#{$1}")
        arr << name unless $1.nil? || arr.include?(name)
      end
    end

    # TODO: Move these methods to lib in case they can be used elsewhere
    def self.get_evm_log_for_date(pattern)
      files = Dir.glob(pattern)
      files.find { |f| f.match(/\/evm\.log/) }
    end

    LOG_TIMESTAMP_REGEX = /\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6})\s#/

    def self.log_timestamp(str)
      return nil unless str

      t  = Time.parse(str)
      Time.utc(t.year, t.month, t.day, t.hour, t.min, t.sec, 0)
    end

    def self.log_duration(filename)
      require 'elif'

      first = File.open(filename, 'r') { |f| find_timestamp(f) }
      last  = Elif.open(filename, 'r') { |f| find_timestamp(f) }

      return first, last
    end

    def self.get_log_start_end_times(filename)
      if filename.nil? || !File.exist?(filename)
        return nil, nil
      elsif filename.ends_with?('.gz')
        log_duration_gz(filename)
      else
        log_duration(filename)
      end
    end

    def self.log_duration_gz(filename)
      require 'zlib'

      begin
        _log.info("Opening filename: [#{filename}], size: [#{File.size(filename)}]")
        Zlib::GzipReader.open(filename) do |gz|
          line_count = 0
          start_time_str = nil
          end_time_str   = nil

          gz.each_line do |line|
            line_count += 1
            next unless line =~ LOG_TIMESTAMP_REGEX

            start_time_str ||= $1
            end_time_str     = $1
          end

          start_time = log_timestamp(start_time_str)
          end_time   = log_timestamp(end_time_str)

          _log.info("Lines in file: [#{line_count}]")
          _log.info("Start Time: [#{start_time.inspect}]")
          _log.info("End   Time: [#{end_time.inspect}]")

          return start_time, end_time
        end
      rescue Exception => e
        _log.error(e.to_s)
        []
      end
    end

    def self.zip_logs(zip_filename, dirs, userid = "system")
      require 'zip/filesystem'
      zip_dir = Rails.root.join("data", "user", userid)
      FileUtils.mkdir_p(zip_dir) unless File.exist?(zip_dir)

      zfile = zip_dir.join(zip_filename)
      File.delete(zfile) if File.exist?(zfile)
      zfile_display = zfile.relative_path_from(Rails.root)

      zfile = zfile.to_s

      _log.info("Creating: [#{zfile_display}]")
      Zip::File.open(zfile, Zip::File::CREATE) do |zip|
        dirs.each do |dir|
          dir = Rails.root.join(dir) unless Pathname.new(dir).absolute?
          Dir.glob(dir).each do |file|
            begin
              entry, _mtime = add_zip_entry(zip, file, zfile)
            rescue => e
              _log.error("Failed to add file: [#{entry}]. Error information: #{e.message}")
            end
          end
        end
        zip.close
      end
      _log.info("Created: [#{zfile_display}], Size: [#{File.size(zfile)}]")

      zfile
    end

    # TODO: Make a class out of this so we don't have to pass around the zip.
    def self.add_zip_entry(zip, file_path, zfile)
      entry = zip_entry_from_path(file_path)
      mtime = File.mtime(file_path)
      ztime = Zip::DOSTime.at(mtime.to_i)
      if File.directory?(file_path)
        zip.mkdir(entry)
      elsif File.symlink?(file_path)
        zip_entry = Zip::Entry.new(zfile, entry, nil, nil, nil, nil, nil, nil, ztime)
        zip.add(zip_entry, File.realpath(file_path))
      else
        zip_entry = Zip::Entry.new(zfile, entry, nil, nil, nil, nil, nil, nil, ztime)
        zip.add(zip_entry, file_path)
        _log.info("Adding file: [#{entry}], size: [#{File.size(file_path)}], mtime: [#{mtime}]")
      end
      return entry, mtime
    end
    private_class_method :add_zip_entry

    def self.zip_entry_from_path(path)
      rails_root_directories = Rails.root.to_s.split("/")
      within_rails_root = path.split("/")[0, rails_root_directories.length] == rails_root_directories
      within_rails_root ? Pathname.new(path).relative_path_from(Rails.root).to_s : "ROOT#{path}"
    end
    private_class_method :zip_entry_from_path

    def self.find_timestamp(handle)
      handle
        .lazy
        .take(250)
        .map { |line| log_timestamp($1) if line =~ LOG_TIMESTAMP_REGEX }
        .reject(&:nil?)
        .first
    end
    private_class_method :find_timestamp
  end
end
