defmodule Towerops.Accounts.Scope do
  @moduledoc """
  Defines the scope of the caller to be used throughout the app.

  The `Towerops.Accounts.Scope` allows public interfaces to receive
  information about the caller, such as if the call is initiated from an
  end-user, and if so, which user. Additionally, such a scope can carry fields
  such as "super user" or other privileges for use as authorization, or to
  ensure specific code paths can only be access for a given scope.

  It is useful for logging as well as for scoping pubsub subscriptions and
  broadcasts when a caller subscribes to an interface or performs a particular
  action.

  Feel free to extend the fields on this struct to fit the needs of
  growing application requirements.
  """

  alias Towerops.Accounts.User
  alias Towerops.Organizations.Organization

  defstruct user: nil, superuser: nil, impersonating?: false, organization: nil, timezone: "UTC", time_format: "24h"

  @doc """
  Creates a scope for the given user.

  Returns nil if no user is given.
  """
  def for_user(%User{} = user) do
    %__MODULE__{
      user: user,
      superuser: nil,
      impersonating?: false,
      timezone: user.timezone || "UTC",
      time_format: user.time_format || "24h"
    }
  end

  def for_user(nil), do: nil

  @doc """
  Creates a scope for a superuser impersonating another user.

  The `user` field contains the impersonated user (what the superuser sees as),
  while the `superuser` field contains the actual superuser performing the impersonation.
  """
  def for_impersonation(%User{} = superuser, %User{} = target_user) do
    %__MODULE__{
      user: target_user,
      superuser: superuser,
      impersonating?: true,
      timezone: target_user.timezone || "UTC",
      time_format: target_user.time_format || "24h"
    }
  end

  @doc """
  Adds organization context to an existing scope.

  Returns updated scope with organization field set.
  """
  def put_organization(%__MODULE__{} = scope, %Organization{} = organization) do
    %{scope | organization: organization}
  end

  def put_organization(%__MODULE__{} = scope, nil) do
    %{scope | organization: nil}
  end

  def put_organization(nil, _organization), do: nil

  @doc """
  Returns true if the scope has superuser privileges.

  This includes both:
  - Regular users with is_superuser = true
  - Superusers who are currently impersonating another user
  """
  def superuser?(%__MODULE__{user: user, superuser: superuser}) do
    (user && user.is_superuser) || (superuser && superuser.is_superuser)
  end

  def superuser?(nil), do: false
end
