defmodule ExoSQL.Node do
  @moduledoc """
  Example extractor that gather information from the system

  Currently only supports the `passwd` table.
  """

  def schema(_db), do: {:ok, ["passwd", "proc"]}

  def schema(_db, "passwd") do
    {:ok,
     %{
       columns: [
         "user",
         "x",
         "uid",
         "gid",
         "name",
         "home",
         "shell"
       ]
     }}
  end

  def schema(_db, "proc") do
    {:ok,
     %{
       columns: [
         "pid",
         "cmd",
         "args"
       ]
     }}
  end

  def execute(_db, "passwd", _quals, _columns) do
    csv_data = File.stream!("/etc/passwd") |> CSV.decode(separator: ?:)

    rows =
      for l <- csv_data do
        {:ok, l} = l
        l
      end

    {:ok,
     %{
       columns: [
         "user",
         "x",
         "uid",
         "gid",
         "name",
         "home",
         "shell"
       ],
       rows: rows
     }}
  end

  def execute(_config, "proc", _quals, columns) do
    known_columns = ["pid", "cmd", "args"]

    for c <- columns do
      if not (c in known_columns) do
        raise MatchError, {:unknown_column, c}
      end
    end

    {:ok, proc} = File.ls("/proc/")
    # Keep only numeric procs
    proc =
      Enum.flat_map(proc, fn n ->
        case Integer.parse(n) do
          {n, ""} -> [n]
          _ -> []
        end
      end)

    rows =
      Enum.map(proc, &{File.open("/proc/#{&1}/cmdline"), &1})
      |> Enum.flat_map(fn
        {{:ok, fd}, pid} ->
          case IO.read(fd, 1024) do
            :eof ->
              []

            data ->
              [cmd | args] = String.split(data, "\0")
              [[pid, cmd, args]]
          end

        _ ->
          []
      end)

    {:ok,
     %{
       columns: known_columns,
       rows: rows
     }}
  end
end
