defmodule Mobilizon.GraphQL.Authorization.AppScope do
  @moduledoc """
  Module referencing all scopes usable in the Mobilizon API
  """

  require Logger

  @global_scope %{
    "write" => [
      # Media
      :"write:media:upload",
      :"write:media:remove",
      # Event permissions
      :"write:event:create",
      :"write:event:update",
      :"write:event:delete",
      # Comment permissions
      :"write:comment:create",
      :"write:comment:update",
      :"write:comment:delete",
      # Event participation permission
      :"write:participation",
      # User account permissions
      :"write:user:settings",
      :"write:user:setting:activity",
      :"write:user:setting:push",
      # Profile permissions
      :"write:profile:create",
      :"write:profile:update",
      :"write:profile:delete",
      :"write:profile:feed_token:create",
      :"write:feed_token:delete",
      # Membership permissions
      :"write:group_membership",
      # Group permissions
      :"write:group:create",
      :"write:group:update",
      :"write:group:delete",
      # Group discussions permissions
      :"write:group:discussion:create",
      :"write:group:discussion:update",
      :"write:group:discussion:delete",
      # Group resources permissions
      :"write:group:resources:create",
      :"write:group:resources:update",
      :"write:group:resources:delete",
      # Group members
      :"write:group:members",
      # Post permissions
      :"write:group:post:create",
      :"write:group:post:update",
      :"write:group:post:delete"
    ],
    "read" => [
      :"read:event",
      :"read:event:participants",
      :"read:event:participants:export",
      :"read:user:settings",
      # Profile permissions
      :"read:profile",
      :"read:profile:organized_events",
      :"read:profile:participations",
      :"read:profile:memberships",
      :"read:profile:follows",
      # Group details permissions
      :"read:group",
      :"read:group:events",
      :"read:group:discussions",
      :"read:group:resources",
      :"read:group:members",
      :"read:group:followers",
      :"read:group:todo_lists",
      :"read:group:activities"
    ]
  }

  @spec get_scopes :: list(atom())
  def get_scopes do
    @global_scope
    |> Map.values()
    |> Enum.concat()
    |> Enum.concat([:read, :write])
  end

  @spec scopes_valid?(String.t()) :: boolean()
  def scopes_valid?(scopes) do
    scopes
    |> String.split(" ")
    |> Enum.all?(&scope_valid?/1)
  end

  @spec scope_valid?(String.t() | atom()) :: boolean()
  def scope_valid?(scope) when is_binary(scope) do
    scope in Enum.map(get_scopes(), &to_string/1)
  end

  def scope_valid?(scope) when is_atom(scope) do
    scope in get_scopes()
  end

  @spec has_app_access?(binary, atom | binary) :: boolean
  def has_app_access?(scope, rule) do
    Logger.debug("Has app token access? scope: #{inspect(scope)}, rule: #{inspect(rule)}")
    scope = String.split(scope, " ")
    scope_acceptable_for_rule?(scope, rule) or global_scopes_acceptable_for_rule?(scope, rule)
  end

  @spec scope_acceptable_for_rule?(list(String.t() | atom()), String.t() | atom()) :: boolean()
  defp scope_acceptable_for_rule?(scope, rule) when is_list(scope) do
    to_string(rule) in Enum.map(scope, &to_string/1)
  end

  defp global_scopes_acceptable_for_rule?(scope, rule),
    do: Enum.any?(scope, &global_scope_acceptable_for_rule?(&1, rule))

  defp global_scope_acceptable_for_rule?(global_scope, rule),
    do: scope_acceptable_for_rule?(Map.get(@global_scope, global_scope, []), rule)
end
