module Sessions::Node

  # get application root directory
  @root = Dir.pwd.to_s
  if @root.blank? || @root == '/'
    @root = Rails.root
  end

  # get working directories
  @path = "#{@root}/tmp/session_node_#{Rails.env}"

  def self.session_assigne(client_id, force = false)

    # get available nodes
    nodes = Sessions::Node.registered
    session_count = {}

    nodes.each do |node|
      count = Sessions::Node.sessions_by(node['node_id'], force).count
      session_count[node['node_id']] = count
    end

    # search for lowest session count
    node_id = nil
    node_count = nil
    session_count.each do |local_node_id, count|
      next if !node_count.nil? && count > node_count

      node_count = count
      node_id = local_node_id
    end

    # assigne session
    Rails.logger.info "Assigne session to node #{node_id} (#{client_id})"
    Sessions::Node.sessions_for(node_id, client_id)

    # write node status file
    node_id
  end

  def self.cleanup
    FileUtils.rm_rf @path
  end

  def self.registered
    path = "#{@path}/*.status"
    nodes = []
    files = Dir.glob(path)
    files.each do |filename|
      File.open(filename, 'rb') do |file|
        file.flock(File::LOCK_SH)
        content = file.read
        file.flock(File::LOCK_UN)
        begin
          data = JSON.parse(content)
          nodes.push data
        rescue => e
          Rails.logger.error "can't parse status file #{filename}, #{e.inspect}"
          #to_delete.push "#{path}/#{entry}"
          #next
        end
      end
    end
    nodes
  end

  def self.register(node_id)
    if !File.exist?(@path)
      FileUtils.mkpath @path
    end

    status_file = "#{@path}/#{node_id}.status"

    # write node status file
    data = {
      updated_at_human: Time.now.utc,
      updated_at:       Time.now.utc.to_i,
      node_id:          node_id.to_s,
      pid:              $PROCESS_ID,
    }
    content = data.to_json

    # store session data in session file
    File.open(status_file, 'wb') do |file|
      file.write content
    end

  end

  def self.stats
    # read node sessions
    path = "#{@path}/*.session"

    sessions = {}
    files = Dir.glob(path)
    files.each do |filename|
      File.open(filename, 'rb') do |file|
        file.flock(File::LOCK_SH)
        content = file.read
        file.flock(File::LOCK_UN)
        begin
          next if content.blank?

          data = JSON.parse(content)
          next if data.blank?
          next if data['client_id'].blank?

          sessions[data['client_id']] = data['node_id']
        rescue => e
          Rails.logger.error "can't parse session file #{filename}, #{e.inspect}"
          #to_delete.push "#{path}/#{entry}"
          #next
        end
      end
    end
    sessions
  end

  def self.sessions_for(node_id, client_id)
    if !File.exist?(@path)
      FileUtils.mkpath @path
    end

    status_file = "#{@path}/#{node_id}.#{client_id}.session"

    # write node status file
    data = {
      updated_at_human: Time.now.utc,
      updated_at:       Time.now.utc.to_i,
      node_id:          node_id.to_s,
      client_id:        client_id.to_s,
      pid:              $PROCESS_ID,
    }
    content = data.to_json

    # store session data in session file
    File.open(status_file, 'wb') do |file|
      file.write content
    end

  end

  def self.sessions_by(node_id, force = false)

    # read node sessions
    path = "#{@path}/#{node_id}.*.session"

    sessions = []
    files = Dir.glob(path)
    files.each do |filename|
      File.open(filename, 'rb') do |file|
        file.flock(File::LOCK_SH)
        content = file.read
        file.flock(File::LOCK_UN)
        begin
          next if content.blank?

          data = JSON.parse(content)
          next if data.blank?
          next if data['client_id'].blank?
          next if !Sessions.session_exists?(data['client_id']) && force == false

          sessions.push data['client_id']
        rescue => e
          Rails.logger.error "can't parse session file #{filename}, #{e.inspect}"
          #to_delete.push "#{path}/#{entry}"
          #next
        end
      end
    end
    sessions
  end

end
