defmodule Ewebmachine.Log do
  alias Plug.Conn
  use GenServer
  @moduledoc false

  def init([]), do:
    (:ets.new(:logs, [:ordered_set, :named_table]); {:ok,[]})
  def handle_cast(conn,_), do:
    (:ets.insert(:logs,{conn.private[:machine_log],conn}); {:noreply,[]})

  # Public API, self describing
  def start_link(_), do: 
    GenServer.start_link(__MODULE__,[], name: __MODULE__)
  def put(conn), do:
    GenServer.cast(__MODULE__,conn)
  def list do # fold only needed file for log listing for perfs
    :ets.foldl(fn {_,%{method: m, request_path: path, path_info: pi, private: %{machine_log: l, machine_init_at: i}}},acc-> 
       [%Conn{method: m,request_path: path, path_info: pi, private: %{machine_log: l,machine_init_at: i}}|acc]
    end,[],:logs)
  end
  def get(id), do: 
    (case :ets.lookup(:logs,id) do [{_,conn}]->conn; _->nil end)
  def id, do:
    (make_ref() |> :erlang.term_to_binary |> Base.url_encode64)

  # Conn modifiers called by automate during run
  def debug_init(conn) do
    if conn.private[:machine_debug] do
      conn
      |> Conn.put_private(:machine_log,id())
      |> Conn.put_private(:machine_init_at,:erlang.timestamp)
      |> Conn.put_private(:machine_decisions,[])
      |> Conn.put_private(:machine_calls,[])
    else conn end
  end
  def debug_call(conn,module,function,[in_conn,in_state],{resp,out_conn,out_state}) do
    if conn.private[:machine_log] !== nil and module !== Ewebmachine.Handlers do
      Conn.put_private(conn,:machine_calls,
        [{module,function,[%{in_conn|private: %{}},in_state],
                           {resp,%{out_conn|private: %{}},out_state}}
            |conn.private.machine_calls])
    else conn end
  end
  def debug_enddecision(conn) do
    if conn.private[:machine_log] do
      case conn.private.machine_decisions do
        [{decision,_}|rest] ->
          conn 
          |> Conn.put_private(:machine_decisions,[{decision,Enum.reverse(conn.private.machine_calls)}|rest])
          |> Conn.put_private(:machine_calls,[])
        _->conn
      end
    else conn end
  end
  def debug_decision(conn,decision) do
    if conn.private[:machine_log] do
      case Regex.run(~r/^v[0-9]([a-z]*[0-9]*)$/,to_string(decision)) do
        [_,decision]-> 
          conn = debug_enddecision(conn)
          Conn.put_private(conn,:machine_decisions,[{decision,[]}|conn.private.machine_decisions])
        _->conn
      end
    else conn end
  end
end
