require_relative 'concerns/sortable_stat'

class HostNodeStat
  include Mongoid::Document
  include Mongoid::Timestamps::Created
  include SortableStat

  field :load, type: Hash
  field :memory, type: Hash
  field :filesystem, type: Array
  field :usage, type: Hash
  field :cpu, type: Hash
  field :network, type: Hash

  belongs_to :grid
  belongs_to :host_node

  index({ grid_id: 1 }, { background: true })
  index({ host_node_id: 1 }, { background: true })
  index({ host_node_id: 1, created_at: 1 }, { background: true })
  index({ grid_id: 1, created_at: 1 }, { background: true })

  def self.get_aggregate_stats_for_node(node_id, from_time, to_time)
    self.collection.with( read: { mode: :secondary_preferred } ).aggregate([
    {
      '$match' => {
        host_node_id: node_id,
        created_at: {
          '$gte' => from_time,
          '$lte' => to_time
        }
      }
    },
    # Flatten filesystem and sum values
    {
      '$unwind' => '$filesystem',
    },
    {
      '$group' => {
        _id: '$_id',
        created_at: { '$first' => '$created_at' },
        cpu: { '$first' => '$cpu' },
        memory: { '$first' => '$memory' },
        network: { '$first' => '$network' },
        filesystem_used: { '$sum' => '$filesystem.used' },
        filesystem_total: { '$sum' => '$filesystem.total' }
      }
    },
    # Aggregate for each minute
    {
      '$group' => {
        _id: {
          year: { '$year' => '$created_at' },
          month: { '$month' => '$created_at' },
          day: { '$dayOfMonth' => '$created_at' },
          hour: { '$hour' => '$created_at' },
          minute: { '$minute' => '$created_at' }
        },
        created_at: { '$first' => '$created_at' },

        cpu_num_cores: { '$avg' => '$cpu.num_cores' },
        cpu_percent_used: {
          '$avg' => {
            '$add' => [
              { '$ifNull' => ['$cpu.user', 0] },
              { '$ifNull' => ['$cpu.system', 0] },
              { '$ifNull' => ['$cpu.nice', 0] }
            ]
          }
        },

        memory_used: { '$avg' => '$memory.used' },
        memory_total: { '$avg' => '$memory.total' },
        memory_free: { '$avg' => '$memory.free' },
        memory_active: { '$avg' => '$memory.active' },
        memory_inactive: { '$avg' => '$memory.inactive' },
        memory_cached: { '$avg' => '$memory.cached' },
        memory_buffers: { '$avg' => '$memory.buffers' },

        network_internal_interfaces: { '$first' => '$network.internal.interfaces' },
        network_internal_rx_bytes: { '$avg' => '$network.internal.rx_bytes' },
        network_internal_rx_bytes_per_second: { '$avg' => '$network.internal.rx_bytes_per_second' },
        network_internal_tx_bytes: { '$avg' => '$network.internal.tx_bytes' },
        network_internal_tx_bytes_per_second: { '$avg' => '$network.internal.tx_bytes_per_second' },
        network_external_interfaces: { '$first' => '$network.external.interfaces' },
        network_external_rx_bytes: { '$avg' => '$network.external.rx_bytes' },
        network_external_rx_bytes_per_second: { '$avg' => '$network.external.rx_bytes_per_second' },
        network_external_tx_bytes: { '$avg' => '$network.external.tx_bytes' },
        network_external_tx_bytes_per_second: { '$avg' => '$network.external.tx_bytes_per_second' },

        filesystem_used: { '$avg' => '$filesystem_used' },
        filesystem_total: { '$avg' => '$filesystem_total' }
      }
    },
    # Sort and project back into useful format
    {
      '$sort' => { 'created_at' => 1 }
    },
    {
      '$project' => {
        _id: 0,
        timestamp: '$_id',
        cpu: {
          num_cores: '$cpu_num_cores',
          percent_used: '$cpu_percent_used'
        },
        memory: {
          used: '$memory_used',
          total: '$memory_total',
          free: '$memory_free',
          active: '$memory_active',
          inactive: '$memory_inactive',
          cached: '$memory_cached',
          buffers: '$memory_buffers'
        },
        network: {
          internal: {
            interfaces: '$network_internal_interfaces',
            rx_bytes: '$network_internal_rx_bytes',
            rx_bytes_per_second: '$network_internal_rx_bytes_per_second',
            tx_bytes: '$network_internal_tx_bytes',
            tx_bytes_per_second: '$network_internal_tx_bytes_per_second'
          },
          external: {
            interfaces: '$network_external_interfaces',
            rx_bytes: '$network_external_rx_bytes',
            rx_bytes_per_second: '$network_external_rx_bytes_per_second',
            tx_bytes: '$network_external_tx_bytes',
            tx_bytes_per_second: '$network_external_tx_bytes_per_second'
          }
        },
        filesystem: {
          used: '$filesystem_used',
          total: '$filesystem_total'
        }
      }
    }])
  end

  def self.get_aggregate_stats_for_grid(grid_id, from_time, to_time)
    self.collection.with( read: { mode: :secondary_preferred } ).aggregate([
    {
      '$match' => {
        grid_id: grid_id,
        created_at: {
          '$gte' => from_time,
          '$lte' => to_time
        }
      }
    },
    # Flatten filesystem and sum values
    {
      '$unwind' => '$filesystem',
    },
    {
      '$group' => {
        _id: '$_id',
        host_node_id: { '$first' => '$host_node_id' },
        created_at: { '$first' => '$created_at' },
        cpu: { '$first' => '$cpu' },
        memory: { '$first' => '$memory' },
        network: { '$first' => '$network' },
        filesystem_used: { '$sum' => '$filesystem.used' },
        filesystem_total: { '$sum' => '$filesystem.total' }
      }
    },
    # Aggregate for each minute
    {
      '$group' => {
        _id: {
          year: { '$year' => '$created_at' },
          month: { '$month' => '$created_at' },
          day: { '$dayOfMonth' => '$created_at' },
          hour: { '$hour' => '$created_at' },
          minute: { '$minute' => '$created_at' },
          host_node_id: '$host_node_id',
        },
        created_at: { '$first' => '$created_at' },

        cpu_num_cores: { '$avg' => '$cpu.num_cores' },
        cpu_percent_used: {
          '$avg' => {
            '$add' => [
              { '$ifNull' => ['$cpu.user', 0] },
              { '$ifNull' => ['$cpu.system', 0] },
              { '$ifNull' => ['$cpu.nice', 0] }
            ]
          }
        },

        memory_used: { '$avg' => '$memory.used' },
        memory_total: { '$avg' => '$memory.total' },
        memory_free: { '$avg' => '$memory.free' },
        memory_active: { '$avg' => '$memory.active' },
        memory_inactive: { '$avg' => '$memory.inactive' },
        memory_cached: { '$avg' => '$memory.cached' },
        memory_buffers: { '$avg' => '$memory.buffers' },

        network_internal_interfaces: { '$first' => '$network.internal.interfaces' },
        network_internal_rx_bytes: { '$avg' => '$network.internal.rx_bytes' },
        network_internal_rx_bytes_per_second: { '$avg' => '$network.internal.rx_bytes_per_second' },
        network_internal_tx_bytes: { '$avg' => '$network.internal.tx_bytes' },
        network_internal_tx_bytes_per_second: { '$avg' => '$network.internal.tx_bytes_per_second' },
        network_external_interfaces: { '$first' => '$network.external.interfaces' },
        network_external_rx_bytes: { '$avg' => '$network.external.rx_bytes' },
        network_external_rx_bytes_per_second: { '$avg' => '$network.external.rx_bytes_per_second' },
        network_external_tx_bytes: { '$avg' => '$network.external.tx_bytes' },
        network_external_tx_bytes_per_second: { '$avg' => '$network.external.tx_bytes_per_second' },

        filesystem_used: { '$avg' => '$filesystem_used' },
        filesystem_total: { '$avg' => '$filesystem_total' }
      }
    },
    # Aggregate nodes (sum fields except CPU%, which is averaged)
    {
      '$group' => {
        _id: {
          year: { '$year' => '$created_at' },
          month: { '$month' => '$created_at' },
          day: { '$dayOfMonth' => '$created_at' },
          hour: { '$hour' => '$created_at' },
          minute: { '$minute' => '$created_at' }
        },
        created_at: { '$first' => '$created_at' },

        cpu_num_cores: { '$sum' => '$cpu_num_cores' },
        cpu_percent_used: { '$sum' => '$cpu_percent_used' },

        memory_used: { '$sum' => '$memory_used' },
        memory_total: { '$sum' => '$memory_total' },
        memory_free: { '$sum' => '$memory_free' },
        memory_active: { '$sum' => '$memory_active' },
        memory_inactive: { '$sum' => '$memory_inactive' },
        memory_cached: { '$sum' => '$memory_cached' },
        memory_buffers: { '$sum' => '$memory_buffers' },

        network_internal_interfaces: { '$first' => '$network_internal_interfaces' },
        network_internal_rx_bytes: { '$sum' => '$network_internal_rx_bytes' },
        network_internal_rx_bytes_per_second: { '$sum' => '$network_internal_rx_bytes_per_second' },
        network_internal_tx_bytes: { '$sum' => '$network_internal_tx_bytes' },
        network_internal_tx_bytes_per_second: { '$sum' => '$network_internal_tx_bytes_per_second' },
        network_external_interfaces: { '$first' => '$network_external_interfaces' },
        network_external_rx_bytes: { '$sum' => '$network_external_rx_bytes' },
        network_external_rx_bytes_per_second: { '$sum' => '$network_external_rx_bytes_per_second' },
        network_external_tx_bytes: { '$sum' => '$network_external_tx_bytes' },
        network_external_tx_bytes_per_second: { '$sum' => '$network_external_tx_bytes_per_second' },

        filesystem_used: { '$sum' => '$filesystem_used' },
        filesystem_total: { '$sum' => '$filesystem_total' }
      }
    },
    # Sort and project back into useful format
    {
      '$sort' => { 'created_at' => 1 }
    },
    {
      '$project' => {
        _id: 0,
        timestamp: '$_id',
        cpu: {
          num_cores: '$cpu_num_cores',
          percent_used: '$cpu_percent_used'
        },
        memory: {
          used: '$memory_used',
          total: '$memory_total',
          free: '$memory_free',
          active: '$memory_active',
          inactive: '$memory_inactive',
          cached: '$memory_cached',
          buffers: '$memory_buffers'
        },
        network: {
          internal: {
            interfaces: '$network_internal_interfaces',
            rx_bytes: '$network_internal_rx_bytes',
            rx_bytes_per_second: '$network_internal_rx_bytes_per_second',
            tx_bytes: '$network_internal_tx_bytes',
            tx_bytes_per_second: '$network_internal_tx_bytes_per_second'
          },
          external: {
            interfaces: '$network_external_interfaces',
            rx_bytes: '$network_external_rx_bytes',
            rx_bytes_per_second: '$network_external_rx_bytes_per_second',
            tx_bytes: '$network_external_tx_bytes',
            tx_bytes_per_second: '$network_external_tx_bytes_per_second'
          }
        },
        filesystem: {
          used: '$filesystem_used',
          total: '$filesystem_total'
        }
      }
    }])
  end
end
