defmodule Chaperon.Export.JSON do
  @moduledoc """
  JSON metrics export module.
  """

  @behaviour Chaperon.Exporter

  alias Chaperon.Scenario.Metrics

  @columns [
             :total_count,
             :max,
             :mean,
             :min
           ] ++ for(p <- Metrics.percentiles(), do: {:percentile, p})

  @doc """
  Encodes metrics of given `session` into JSON format.
  """
  @impl Chaperon.Exporter
  def encode(session, _opts \\ []) do
    data =
      session.metrics
      |> Enum.map(fn
        {{:call, {mod, func}}, vals} ->
          %{
            action: :call,
            module: inspect(mod),
            function: func,
            metrics: metrics(vals)
          }

        {{{:error, {:http, code}}, {action, url}}, vals} ->
          %{
            error: %{http: code},
            action: action,
            url: url,
            metrics: metrics(vals)
          }

        {{action, url}, vals} ->
          %{action: action, url: url, metrics: metrics(vals)}

        {action, vals} ->
          %{action: action, metrics: metrics(vals)}
      end)
      |> Poison.encode!()

    {:ok, data}
  end

  @impl Chaperon.Exporter
  def write_output(lt_mod, options, data, filename) do
    runtime_config = Keyword.get(options, :config, %{})
    Chaperon.write_output_to_file(lt_mod, runtime_config, data, filename <> ".json")
  end

  def metrics([]), do: %{}

  def metrics([v | vals]) do
    Map.merge(metrics(v), metrics(vals))
  end

  def metrics(vals) when is_map(vals) do
    metrics =
      vals
      |> Map.take(@columns)
      |> Enum.map(fn
        {{:percentile, p}, val} ->
          {"percentile_#{p}", round(val)}

        {k, v} ->
          {k, round(v)}
      end)
      |> Enum.into(%{})

    session_name = vals[:session_name]

    if session_name && String.trim(session_name) != "" do
      %{vals[:session_name] => metrics}
    else
      metrics
    end
  end
end
