# Returns status information about the running server, including software
# versions, basic load info, and Redis and Postgres info.
#
# @see StatusController
# @see https://danbooru.donmai.us/status
class ServerStatus
  extend Memoist
  include ActiveModel::Serializers::JSON
  include ActiveModel::Serializers::Xml

  attr_reader :request

  def initialize(request)
    @request = request
  end

  def serializable_hash(options = {})
    {
      ip: request.remote_ip,
      headers: http_headers,
      status: {
        hostname: hostname,
        uptime: uptime,
        loadavg: loadavg,
        danbooru_version: danbooru_version,
        ruby_version: RUBY_VERSION,
        distro_version: distro_version,
        kernel_version: kernel_version,
        libvips_version: libvips_version,
        ffmpeg_version: ffmpeg_version,
        mkvmerge_version: mkvmerge_version,
        redis_version: redis_version,
        postgres_version: postgres_version,
      },
      postgres: {
        connection_stats: postgres_connection_stats,
      },
      redis: {
        info: redis_info,
      }
    }
  end

  concerning :InfoMethods do
    def http_headers
      headers = request.headers.env.select { |key| key.starts_with?("HTTP_") }
      headers = headers.transform_keys { |key| key.delete_prefix("HTTP_").titleize.tr(" ", "-") }
      headers = headers.except("Cookie")
      headers = headers.reject { |k, v| v.blank? }
      headers
    end

    def hostname
      Socket.gethostname
    end

    def uptime
      seconds = File.read("/proc/uptime").split[0].to_f
      "#{seconds.seconds.in_days.round} days"
    end

    def loadavg
      File.read("/proc/loadavg").chomp
    end

    def danbooru_version
      Rails.application.config.x.git_hash
    end

    def kernel_version
      File.read("/proc/version").chomp
    end

    def distro_version
      `. /etc/os-release; echo "$NAME $VERSION"`.chomp
    end

    def libvips_version
      Vips::LIBRARY_VERSION
    end

    def ffmpeg_version
      version = `ffmpeg -version`
      version[/ffmpeg version ([0-9.]+)/, 1]
    end

    def mkvmerge_version
      `mkvmerge --version`.chomp
    end
  end

  concerning :RedisMethods do
    def redis_info
      return {} if Rails.cache.try(:redis).nil?
      Rails.cache.redis.info
    end

    def redis_used_memory
      redis_info["used_memory_rss_human"]
    end

    def redis_version
      redis_info["redis_version"]
    end
  end

  concerning :PostgresMethods do
    def postgres_version
      ApplicationRecord.connection.select_value("SELECT version()")
    end

    def postgres_active_connections
      ApplicationRecord.connection.select_value("SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active'")
    end

    def postgres_connection_stats
      run_query("SELECT pid, state, query_start, state_change, xact_start, backend_start, backend_type FROM pg_stat_activity ORDER BY state, query_start DESC, backend_type")
    end

    def run_query(query)
      result = ApplicationRecord.connection.select_all(query)
      serialize_result(result)
    end

    def serialize_result(result)
      result.rows.map do |row|
        row.each_with_index.map do |col, i|
          [result.columns[i], col]
        end.to_h
      end
    end
  end

  memoize :redis_info
end
