lib/live_phone/util.ex

defmodule LivePhone.Util do
  alias LivePhone.Country

  @doc ~S"""
  This is used to verify a given phone number and see if it is a valid number
  according to ExPhoneNumber.

  ## Examples

      iex> Util.valid?("")
      false

      iex> Util.valid?("+1555")
      false

      iex> Util.valid?("+1555")
      false

      iex> Util.valid?("+1 (555) 555-1234")
      false

      iex> Util.valid?("+1 (555) 555-1234")
      false

      iex> Util.valid?("+1 (650) 253-0000")
      true

      iex> Util.valid?("+16502530000")
      true

  """
  @spec valid?(String.t()) :: boolean()
  def valid?(phone) do
    case ExPhoneNumber.parse(phone, nil) do
      {:ok, parsed_phone} -> ExPhoneNumber.is_valid_number?(parsed_phone)
      _ -> false
    end
  end

  @doc ~S"""
  This is used to try and get a `Country` for a given phone number.

  ## Examples

      iex> Util.get_country("")
      {:error, :invalid_number}

      iex> Util.get_country("+1555")
      {:error, :invalid_number}

      iex> Util.get_country("+1555")
      {:error, :invalid_number}

      iex> Util.get_country("+1 (555) 555-1234")
      {:error, :invalid_number}

      iex> Util.get_country("+1 (555) 555-1234")
      {:error, :invalid_number}

      iex> Util.get_country("+1 (650) 253-0000")
      {:ok, %LivePhone.Country{code: "US", flag_emoji: "🇺🇸",
        name: "United States of America (the)", preferred: false, region_code: "1"}}

      iex> Util.get_country("+16502530000")
      {:ok, %LivePhone.Country{code: "US", flag_emoji: "🇺🇸",
        name: "United States of America (the)", preferred: false, region_code: "1"}}

  """
  @spec get_country(String.t()) :: {:ok, Country.t()} | {:error, :invalid_number}
  def get_country(phone) do
    with {:ok, parsed_phone} <- ExPhoneNumber.parse(phone, nil),
         true <- ExPhoneNumber.is_valid_number?(parsed_phone),
         {:ok, country} <- Country.get(parsed_phone) do
      {:ok, country}
    else
      _ -> {:error, :invalid_number}
    end
  end

  @doc ~S"""
  This is used to normalize a given `phone` number to E.164 format, and returns
  a tuple with `{:ok, formatted_phone}` for valid numbers and `{:error,
  unformatted_phone}` for invalid numbers.

  ## Examples

      iex> Util.normalize("1234", nil)
      {:error, "1234"}

      iex> Util.normalize("+1234", nil)
      {:ok, "+1234"}

      iex> Util.normalize("+1 (650) 253-0000", "US")
      {:ok, "+16502530000"}

  """
  @spec normalize(String.t(), String.t()) :: {:ok, String.t()} | {:error, String.t()}
  def normalize(phone, country) do
    phone
    |> String.replace(~r/[^+\d]/, "")
    |> ExPhoneNumber.parse(country)
    |> case do
      {:ok, result} -> {:ok, ExPhoneNumber.format(result, :e164)}
      _ -> {:error, phone}
    end
  end

  @doc ~S"""
  Parses the given `country_code` into an emoji, but I should note that the
  emoji is not validated so it might return an invalid emoji (this will also
  depend on the unicode version supported by your operating system, and which
  flags are included.)

  ## Examples

      iex> Util.emoji_for_country(nil)
      ""

      iex> Util.emoji_for_country("US")
      "🇺🇸"

  """
  @spec emoji_for_country(String.t() | nil) :: String.t()
  def emoji_for_country(nil), do: ""

  def emoji_for_country(country_code) do
    country_code
    |> String.upcase()
    |> String.to_charlist()
    |> Enum.map(&(&1 - 65 + 127_462))
    |> List.to_string()
  end
end