defmodule ChromeRemoteInterface.HTTP do
  @moduledoc """
  This module handles communicating with the DevTools HTTP JSON API.
  """

  @type success_http_response :: {:ok, Map.t()}
  @type error_http_response :: {:error, any()}

  @spec call(ChromeRemoteInterface.Server.t(), String.t()) ::
          success_http_response | error_http_response
  def call(server, path) do
    server
    |> execute_request(path)
    |> handle_response()
  end

  # ---
  # Private
  # ---

  defp http_url(server, path) do
    "http://#{server.host}:#{server.port}#{path}"
  end

  defp execute_request(server, path) do
    :hackney.request(:get, http_url(server, path), [], <<>>, path_encode_fun: & &1)
  end

  defp handle_response({:ok, status_code, _response_headers, client_ref}) do
    with true <- status_code >= 200 && status_code < 300,
         {:ok, body} <- :hackney.body(client_ref),
         {:ok, formatted_body} <- format_body(body),
         {:ok, json} <- decode(formatted_body) do
      {:ok, json}
    else
      error -> error
    end
  end

  defp handle_response({:error, {:closed, _}}) do
    {:error, :unexpected_close}
  end

  defp handle_response({:error, reason}) do
    {:error, reason}
  end

  defp format_body(""), do: format_body("{}")
  defp format_body(body), do: {:ok, body}

  defp decode(body) do
    case Jason.decode(body) do
      {:ok, json} -> {:ok, json}
      {:error, _reason} -> {:ok, body}
    end
  end
end
