lib/helper/sender.ex

defmodule MishkaInstaller.Helper.Sender do
  @moduledoc """
  To begin, we look at the `hex.pm` website in an effort to get some fundamental information.
  Despite this, after several versions of MishkaInstaller have been released,
  this API may prove to be helpful for managing packages from within an administrative panel.

  ### Reference
   - https://github.com/hexpm/hexpm/issues/1124
  """
  @request_name SenderClientApi
  alias MishkaInstaller.Helper.Extra

  @type app :: map()

  @doc """
  This is an executor function and has several different modes, including:

  1. Get information from `hex.pm` website
  2. Getting information from GitHub
  3. Getting information from the latest GitHub releases
  4. Get information from the latest GitHub tags

  ## Examples

  ```elixir
  MishkaInstaller.Helper.Sender.package("hex", %{"app" => app})
  # or
  MishkaInstaller.Helper.Sender.package("github", %{"url" => app.url, "tag" => app.tag})
  # or
  MishkaInstaller.Helper.Sender.package("github_latest_release", json_data["url"])
  # or
  MishkaInstaller.Helper.Sender.package("github_latest_tag", json_data["url"])
  ```
  """
  @spec package(String.t(), app()) ::
          list
          | {:error, :package, :mix_file | :not_found | :not_tag | :unhandled}
          | {:ok, :package, any}
  def package("hex", %{"app" => name} = _app) do
    send_build(:get, "https://hex.pm/api/packages/#{name}")
  end

  def package("github", %{"url" => url, "tag" => tag} = _app)
      when not is_nil(url) and not is_nil(tag) do
    new_url =
      String.replace(
        MishkaInstaller.trim_url(url),
        "https://github.com/",
        "https://raw.githubusercontent.com/"
      ) <> "/#{String.trim(tag)}/mix.exs"

    send_build(:get, new_url, :normal)
    |> get_basic_information_form_github(String.trim(tag))
  end

  def package("github_latest_release", url) do
    new_url =
      String.replace(
        MishkaInstaller.trim_url(url),
        "https://github.com/",
        "https://api.github.com/repos/"
      ) <>
        "/releases/latest"

    send_build(:get, new_url)
  end

  def package("github_latest_tag", url) do
    new_url =
      String.replace(
        MishkaInstaller.trim_url(url),
        "https://github.com/",
        "https://api.github.com/repos/"
      ) <>
        "/tags"

    send_build(:get, new_url)
  end

  def package(_status, _app), do: {:error, :package, :not_tag}

  defp send_build(:get, url, request \\ :json) do
    Finch.build(:get, url)
    |> Finch.request(@request_name)
    |> request_handler(request)
  end

  defp request_handler({:ok, %Finch.Response{body: body, headers: _headers, status: 200}}, :json) do
    {:ok, :package, Jason.decode!(body)}
  end

  defp request_handler(
         {:ok, %Finch.Response{body: body, headers: _headers, status: 200}},
         :normal
       ) do
    {:ok, :package, body}
  end

  defp request_handler({:ok, %Finch.Response{status: 404}}, _), do: {:error, :package, :not_found}

  defp request_handler(_outputs, _), do: {:error, :package, :unhandled}

  defp get_basic_information_form_github({:ok, :package, body}, tag) do
    case Code.string_to_quoted(body) do
      {:ok, ast} ->
        Extra.ast_mix_file_basic_information(ast, [:app, :version, :source_url], [{:tag, tag}])

      _ ->
        {:error, :package, :mix_file}
    end
  end

  defp get_basic_information_form_github(output, _tag), do: output
end