defmodule Glific.Clients.ReapBenefit do
  @moduledoc """
  Tweak GCS Bucket name based on group that the contact is in (if any)
  """

  import Ecto.Query, warn: false

  alias Glific.{
    Flows.Flow,
    Repo
  }

  @frappe_open_civic_api_url "https://solveninja.org/api/resource/"
  @frappe_open_civic_location_api "https://solveninja.org/api/method/open_civic_backend.api.location.new"

  @doc """
  In the case of RB we retrieve the flow name of the object (id any)
  and set that as the directory name
  """
  @spec gcs_file_name(map()) :: String.t()
  def gcs_file_name(media) do
    if media["flow_id"] do
      flow_name =
        Flow
        |> where([f], f.id == ^media["flow_id"])
        |> select([f], f.name)
        |> Repo.one()

      if flow_name in [nil, ""],
        do: media["remote_name"],
        else: flow_name <> "/" <> media["remote_name"]
    else
      media["remote_name"]
    end
  end

  @doc """
  Create a webhook with different signatures, so we can easily implement
  additional functionality as needed
  """
  @spec webhook(String.t(), map()) :: map()
  def webhook("frappe_check_existing_user", fields) do
    token = fields["token"]
    header = get_header(token)

    url = @frappe_open_civic_api_url <> "User/" <> fields["contact"]["phone"] <> "@solveninja.org"

    Tesla.get(url, headers: header)
    |> case do
      {:ok, %Tesla.Env{status: 200, body: body}} ->
        Jason.decode!(body)
        |> to_minimal_map("User")
        |> Map.merge(%{is_valid: true})

      {:ok, %Tesla.Env{status: 404, body: body}} ->
        error_msg = Jason.decode!(body)
        %{is_valid: false, response: error_msg["exc_type"]}

      {_status, _response} ->
        %{is_valid: false, response: "Invalid response"}
    end
  end

  def webhook("frappe_create_new_user", fields) do
    token = fields["token"]
    header = get_header(token)

    name =
      if fields["contact"]["preferred_name"] in [nil, ""],
        do: String.trim(fields["contact"]["name"]),
        else: String.trim(fields["contact"]["preferred_name"])

    name = name |> String.split() |> Enum.join("")

    body =
      %{
        "email" => fields["contact"]["phone"] <> "@solveninja.org ",
        "first_name" => name,
        "mobile_no" => fields["contact"]["phone"],
        "username" => name,
        "location" => fields["contact"]["Administrative_area_level_3"]
      }
      |> Jason.encode!()

    url = @frappe_open_civic_api_url <> "User"

    Tesla.post(url, body, headers: header)
    |> case do
      {:ok, %Tesla.Env{status: 200, body: body}} ->
        Jason.decode!(body)
        |> to_minimal_map("User")
        |> Map.merge(%{is_valid: true})

      {:ok, %Tesla.Env{status: 409}} ->
        %{is_valid: false, response: "Duplicate User"}

      {_status, _response} ->
        %{is_valid: false, response: "Invalid response"}
    end
  end

  def webhook("frappe_add_location", fields) do
    token = fields["token"]
    header = get_header(token)
    body = Jason.encode!(fields)

    Tesla.post(@frappe_open_civic_location_api, body, headers: header)
    |> case do
      {:ok, %Tesla.Env{status: 200, body: body}} ->
        Jason.decode!(body)
        |> to_minimal_map("Locations")
        |> Map.merge(%{is_valid: true})

      {_status, _response} ->
        %{is_valid: false, response: "Invalid response"}
    end
  end

  def webhook("frappe_add_event", fields) do
    token = fields["token"]
    header = get_header(token)
    body = Jason.encode!(fields)
    url = @frappe_open_civic_api_url <> "Events"

    Tesla.post(url, body, headers: header)
    |> case do
      {:ok, %Tesla.Env{status: 200, body: body}} ->
        Jason.decode!(body)
        |> to_minimal_map("Events")
        |> Map.merge(%{is_valid: true})

      {_status, _response} ->
        %{is_valid: false, response: "Invalid response"}
    end
  end

  def webhook("fetch_from_frappe", fields) do
    with %{is_valid: true, attrs: attrs} <- validate_fetch_attrs(fields) do
      {doctype, doctype_id, token} = attrs
      header = get_header(token)
      url = @frappe_open_civic_api_url <> "#{doctype}/" <> doctype_id

      Tesla.get(url, headers: header)
      |> case do
        {:ok, %Tesla.Env{status: 200, body: body}} ->
          Jason.decode!(body)
          |> to_minimal_map(doctype)
          |> Map.merge(%{is_valid: true})

        {:ok, %Tesla.Env{status: 404, body: body}} ->
          error_msg = Jason.decode!(body)

          %{is_valid: false, response: error_msg["exc_type"]}

        {_status, _response} ->
          %{is_valid: false, response: "Invalid response"}
      end
    end
  end

  def webhook(_, _fields), do: %{}

  @spec get_header(String.t()) :: list()
  defp get_header(token) do
    [
      {"Authorization", token},
      {"Content-Type", "application/json"}
    ]
  end

  @spec validate_fetch_attrs(map()) :: map()
  defp validate_fetch_attrs(
         %{
           "doctype" => doctype,
           "doctype_id" => doctype_id,
           "token" => token
         } = _fields
       )
       when doctype in ["User", "Locations", "Events", "Assets"] do
    %{is_valid: true, attrs: {doctype, doctype_id, token}}
  end

  defp validate_fetch_attrs(_fields),
    do: %{is_valid: false, message: "Add Doctype, Doctype_id and Token in body to proceed"}

  @spec to_minimal_map(map(), String.t()) :: map()
  defp to_minimal_map(%{"data" => data} = _body, "User"),
    do: Map.take(data, ["name", "email", "first_name", "last_name", "gender", "mobile_no"])

  defp to_minimal_map(%{"message" => message} = _body, "Locations"),
    do: Map.take(message, ["name", "latitude", "longitude", "city", "state", "district"])

  defp to_minimal_map(%{"data" => data} = _body, "Events"),
    do: Map.take(data, ["name", "title", "type", "status", "category", "subcategory"])

  defp to_minimal_map(_data, _doctype), do: %{}
end
