defmodule Ejabberd.Config.EjabberdModule do
  @moduledoc """
  Module representing a module block in the configuration file.
  It offers functions for validation and for starting the modules.

  Warning: The name is EjabberdModule to not collide with
  the already existing Elixir.Module.
  """

  alias Ejabberd.Config.EjabberdModule
  alias Ejabberd.Config.Validation
  alias Ejabberd.Config.Attr

  @type t :: %{module: atom, attrs: [Attr.attr]}

  defstruct [:module, :attrs]

  @doc """
  Given a list of modules / single module
  it runs different validators on them.

  For each module, returns a {:ok, mod} or {:error, mod, errors}
  """
  def validate(modules) do
    Validation.validate(modules)
  end

  @doc """
  Given a list of modules, it takes only the ones with
  a git attribute and tries to fetch the repo,
  then, it install them through :ext_mod.install/1
  """
  def fetch_git_repos(modules) do
    modules
    |> Enum.filter(&is_git_module?/1)
    |> Enum.each(&fetch_and_install_git_module/1)
  end

  # Private API

  defp is_git_module?(%EjabberdModule{attrs: attrs}) do
    case Keyword.get(attrs, :git) do
      "" -> false
      repo -> String.match?(repo, ~r/((git|ssh|http(s)?)|(git@[\w\.]+))(:(\/\/)?)([\w\.@\:\/\-~]+)(\.git)(\/)?/)
    end
  end

  defp fetch_and_install_git_module(%EjabberdModule{attrs: attrs}) do
    repo = Keyword.get(attrs, :git)
    mod_name = case Keyword.get(attrs, :name) do
      "" -> infer_mod_name_from_git_url(repo)
      name -> name
    end

    path = "#{:ext_mod.modules_dir()}/sources/ejabberd-contrib\/#{mod_name}"
    fetch_and_store_repo_source_if_not_exists(path, repo)
    :ext_mod.install(mod_name) # Have to check if overwrites an already present mod
  end

  defp fetch_and_store_repo_source_if_not_exists(path, repo) do
    unless File.exists?(path) do
      IO.puts "[info] Fetching: #{repo}"
      :os.cmd(~c"git clone #{repo} #{path}")
    end
  end

  defp infer_mod_name_from_git_url(repo),
    do: String.split(repo, "/") |> List.last |> String.replace(".git", "")
end
