defmodule Glific.Providers.GupshupEnterprise.Template do
  @moduledoc """
  Module for handling template operations specific to Gupshup
  """

  @behaviour Glific.Providers.TemplateBehaviour
  alias Glific.{
    Partners,
    Repo,
    Settings.Language,
    Templates,
    Templates.SessionTemplate
  }

  @template_status %{
    "ENABLED" => "APPROVED",
    "REJECTED" => "REJECTED"
  }

  @doc """
  Submitting HSM template for approval
  """
  @spec submit_for_approval(map()) :: {:ok, SessionTemplate.t()} | {:error, any()}
  def submit_for_approval(attrs),
    do: {:ok, Templates.get_session_template!(attrs.id)}

  @doc """
  Import pre approved templates when BSP is GupshupEnterprise
  """
  @spec import_templates(non_neg_integer(), String.t()) :: {:ok, any}
  def import_templates(organization_id, data) do
    {:ok, stream} = StringIO.open(data)
    organization = Partners.organization(organization_id)

    stream
    |> IO.binstream(:line)
    |> CSV.decode(headers: true, field_transform: &String.trim/1)
    |> Enum.map(fn {_, data} -> import_approved_templates(data) end)
    |> Templates.update_hsms(organization)

    {:ok, %{message: "All templates have been added"}}
  end

  @doc """
  Bulk apply templates from CSV when BSP is GupshupEnterprise
  """
  @spec bulk_apply_templates(non_neg_integer(), String.t()) :: {:ok, any}
  def bulk_apply_templates(_organization_id, _data) do
    {:ok, %{message: "Feature not available"}}
  end

  @doc """
  Updating HSM templates for an organization
  """
  @spec update_hsm_templates(non_neg_integer()) :: :ok | {:error, String.t()}
  def update_hsm_templates(_organization_id) do
    ## We still need to implement this functionality.
    ## Currently it's just for the same behaviour perspective
    ## so that we don't face any issue while fetching the hsm templates
    :ok
  end

  @doc """
  Delete template from the gupshup
  """
  @spec delete(non_neg_integer(), map()) :: {:ok, any()} | {:error, any()}
  def delete(_org_id, attrs) do
    {:ok, attrs}
  end

  @spec import_approved_templates(map()) :: map()
  defp import_approved_templates(template) do
    cleaned_body = String.replace(template["BODY"], "\n\r\n", "\r\n")
    template = Map.put(template, "BODY", cleaned_body)
    updated_body = check_for_button_template(template, template["BUTTONTYPE"])

    %{
      "id" => Ecto.UUID.generate(),
      "data" => updated_body,
      "meta" => get_example_body(template["BODY"]),
      "category" => "UTILITY",
      "elementName" => template["NAME"],
      "languageCode" => get_language(template["LANGUAGE"]),
      "templateType" => template["TYPE"],
      "status" => Map.get(@template_status, template["STATUS"], "PENDING"),
      "bsp_id" => template["TEMPLATEID"]
    }
  end

  @spec check_for_button_template(map(), String.t()) :: String.t()
  defp check_for_button_template(template, "NONE"), do: template["BODY"]

  defp check_for_button_template(template, "CALL_TO_ACTION") do
    button_list = [template["BUTTON1"]] ++ [template["BUTTON2"]] ++ [template["BUTTON3"]]

    button_text =
      Enum.reduce(button_list, "", fn button, acc ->
        if button == "" do
          acc <> ""
        else
          parsed_button = Jason.decode!(button)
          type = parsed_button["type"]
          value = if type == "URL", do: parsed_button["url"], else: parsed_button["phone_number"]
          acc <> "| " <> "[#{parsed_button["text"]},#{value}] "
        end
      end)

    template["BODY"] <> button_text
  end

  defp check_for_button_template(template, "QUICK_REPLY") do
    button_list = [template["BUTTON1"]] ++ [template["BUTTON2"]] ++ [template["BUTTON3"]]

    button_text =
      Enum.reduce(button_list, "", fn button, acc ->
        if button == "" do
          acc <> ""
        else
          parsed_button = Jason.decode!(button)
          acc <> "| " <> "[#{parsed_button["text"]}] "
        end
      end)

    template["BODY"] <> button_text
  end

  @spec get_language(String.t()) :: String.t()
  defp get_language(label_locale) do
    Repo.fetch_by(Language, %{label_locale: label_locale})
    |> case do
      {:ok, language} -> language.locale
      # Setting en as default locale
      {:error, _language} -> "en"
    end
  end

  @spec get_example_body(String.t()) :: String.t()
  defp get_example_body(body) do
    body
    |> String.replace("{{", "[sample text ")
    |> String.replace("}}", "]")
    |> then(&Map.put(%{}, :example, &1))
    |> Jason.encode!()
  end
end
