#!/usr/bin/env ruby
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')

require 'bundler/setup'
require 'zip'
require 'pp'
require 'yajl/json_gem'
require 'optparse'
require 'rcs-common/diagnosticable'
require 'rcs-common/path_utils'
require 'rcs-common/fixnum'

$options = {}

OptionParser.new do |parser|
  parser.on("-o", "--output PATH", "Optionally specify the output file path") { |value|
    $options[:output] = value
  }

  parser.on("--hide-addresses", "Optionally mask ip addresses and domain names that can appear in the output") { |value|
    $options[:hide_addresses] = true
  }

  parser.on("-a", "--agent NAME", "Collect diagnostic info for a specific agent") { |value|
    $options[:agent_name] = value
  }

  parser.on("-h", "--help", "Show this message") {
    puts parser
    exit
  }

  parser.on("--log-level INFO|DEBUG", "Change the log devel") { |value|
    value.upcase!
    raise "Ivalid log level #{value}" unless %w[INFO DEBUG].include?(value)
    $options[:log_level] = value
  }

  parser.on("--version", "Print the RCS version") do
    $options[:version] = true
  end
end.parse!

include RCS::Diagnosticable

if level = $options[:log_level]
  puts "Changing log level to #{level}"
  change_trace_level(level)
  exit
end

if $options[:version]
  version, build = get_version_info
  puts "RCS version #{version} build #{build}"
  exit
end

require_release 'rcs-db/db_layer'
require_release 'rcs-db/license'

RCS::DB::DB.instance.connect

zipname = $options[:output] || File.basename(__FILE__)
zipname << ".zip" unless zipname =~ /zip$/i

puts "Execution directory is #{execution_directory}"
puts "Generating file #{zipname}. It may take a while."

def pretty_print_sync_evidence(evidence, out = $stdout)
  data = evidence.data
  hash = {}
  hash[:started_at]  = data['started_at'] ? Time.at(data['started_at']).utc : ''
  hash[:ended_at]    = data['ended_at'] ? Time.at(data['ended_at']).utc : ''
  hash[:elapsed]     = data['ended_at'] && data['started_at'] ?  (data['ended_at'] - data['started_at']).round(3) : ''
  hash[:total]       = data['total'] || ''
  hash[:count]       = data['count'] || ''
  hash[:speed]       = data['speed'] ? (data['speed'].to_s_bytes+"/s") : ''
  hash[:timeout]     = data['timeout'] ? 'yes' : 'no'
  hash[:size]        = data['size'] ? data['size'].to_s_bytes : ''
  hash[:ip]          = hide_addresses(data['ip'] || data['content'] || '')
  sizes = {started_at: 25, ended_at: 25, elapsed: 10, total: 6, count: 6, speed: 20, timeout: 9, size: 13, ip: 20}
  out.write hash.keys.map { |k| hash[k].to_s.ljust(sizes[k]) }.join('|')+"\n"
end

if agent_name = $options[:agent_name]
  agent = Item.agents.where(name: agent_name).first
  raise "Unable to find agent named #{agent_name}" unless agent

  Zip::OutputStream.open(zipname) do |out|
    folder = "agent_#{agent.id}"

    out.put_next_entry("#{folder}/attributes.json")
    attributes = agent.attributes.reject { |key| %w[logkey confkey configs stat].include?(key) }
    pretty_print(attributes, out)

    out.put_next_entry("#{folder}/sync_history")
    ::Evidence.target(agent.get_parent).where(type: 'sync', aid: agent.id.to_s).order_by([[:da, :asc]]).each do |evidence|
      pretty_print_sync_evidence(evidence, out)
    end

    out.put_next_entry("#{folder}/stat.json")
    pretty_print(agent.stat.attributes, out)

    agent.configs.each do |config|
      name = Time.at(config.saved).utc.strftime("%Y_%m_%d__%H_%M_%S_utc")
      out.put_next_entry("#{folder}/configs/#{name}.json")
      pretty_print(config.attributes, out)
    end
  end

  exit
end

buffer = Zip::OutputStream.write_buffer do |out|
  [::Status, ::Collector, ::Core].each do |klass|
    out.put_next_entry("collections/"+klass.collection_name.to_s+".json")
    klass.all.each do |document|
      pretty_print(document.attributes, out)
    end
  end

  relevant_logs.each do |path|
    if huge_log?(path)
      puts "Warning: #{path} is too big"
      next
    end

    entry = path[path.index('log/')..-1]
    out.put_next_entry(entry)
    out.write(hide_addresses(File.read(path)))
  end

  out.put_next_entry("machine")
  pretty_print(machine_info, out)

  config_files.each do |path|
    entry = path[path.index('config/')..-1]
    out.put_next_entry(entry)
    content = File.read(path)
    content = hide_addresses(content) unless %w[.pem .crt].include?(File.extname(path))
    out.write(content)
  end

  hosts_path = 'C:\Windows\system32\drivers\etc\hosts'
  if File.exists?(hosts_path)
    out.put_next_entry('hosts')
    out.write(File.read(hosts_path))
  end

  out.put_next_entry("counts")
  out.puts("Item count: #{Item.all.count}")
  [AlertQueue, PushQueue, OCRQueue, TransQueue, AggregatorQueue, IntelligenceQueue].each do |klass|
    out.puts("#{klass.name} count: #{klass.where(flag: 0).count}")
  end
  out.puts("License counters:")
  pretty_print(LicenseManager.instance.counters, out)

  ['rcs-worker-queue', 'rcs-worker-stats', 'rcs-db-stats'].each do |command|
    out.put_next_entry(command)
    out.puts(hide_addresses(command_output(command)))
  end

  out.put_next_entry('rcs-db-status_frontend')
  out.puts(hide_addresses(command_output('rcs-db-status -f')))

  out.put_next_entry('rcs-db-status_backend')
  out.puts(hide_addresses(command_output('rcs-db-status -b')))

  out.put_next_entry('rcs-db-status_system')
  out.puts(hide_addresses(command_output('rcs-db-status -s')))

  if windows?
    out.put_next_entry("systeminfo")
    out.puts(hide_addresses(command_output("systeminfo")))
  end
end

File.open(zipname, "wb") {|f| f.write(buffer.string) }
puts "#{buffer.size} byte(s) written"
