defmodule MvWeb.LiveHelpers do
  @moduledoc """
  Shared LiveView lifecycle hooks and helper functions.

  ## on_mount Hooks
  - `:default` - Sets the user's locale from session (defaults to "de")
  - `:ensure_user_role_loaded` - Ensures current_user has role relationship loaded
  - `:check_page_permission_on_params` - Attaches handle_params hook to enforce page permission on client-side navigation (push_patch)

  ## Usage
  Add to LiveView modules via:
  ```elixir
  on_mount {MvWeb.LiveHelpers, :default}
  on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
  on_mount {MvWeb.LiveHelpers, :check_page_permission_on_params}
  ```
  """
  import Phoenix.Component
  alias MvWeb.Plugs.CheckPagePermission

  def on_mount(:default, _params, session, socket) do
    locale = session["locale"] || "de"
    Gettext.put_locale(locale)
    {:cont, socket}
  end

  def on_mount(:ensure_user_role_loaded, _params, _session, socket) do
    socket = ensure_user_role_loaded(socket)
    {:cont, socket}
  end

  def on_mount(:check_page_permission_on_params, _params, _session, socket) do
    {:cont,
     Phoenix.LiveView.attach_hook(
       socket,
       :check_page_permission,
       :handle_params,
       &check_page_permission_handle_params/3
     )}
  end

  defp check_page_permission_handle_params(_params, uri, socket) do
    path = uri |> URI.parse() |> Map.get(:path, "/") || "/"

    if CheckPagePermission.public_path?(path) do
      {:cont, socket}
    else
      user = socket.assigns[:current_user]
      host = uri |> URI.parse() |> Map.get(:host) || "localhost"

      if CheckPagePermission.user_can_access_page?(user, path, router: MvWeb.Router, host: host) do
        {:cont, socket}
      else
        redirect_to = CheckPagePermission.redirect_target_for_user(user)

        socket =
          socket
          |> Phoenix.LiveView.put_flash(:error, "You don't have permission to access this page.")
          |> Phoenix.LiveView.push_navigate(to: redirect_to)

        {:halt, socket}
      end
    end
  end

  defp ensure_user_role_loaded(socket) do
    user = socket.assigns[:current_user]

    if user do
      # Use centralized Actor helper to ensure role is loaded
      user_with_role = Mv.Authorization.Actor.ensure_loaded(user)
      assign(socket, :current_user, user_with_role)
    else
      socket
    end
  end

  @doc """
  Helper function to get the current actor (user) from socket assigns.

  Provides consistent access pattern across all LiveViews.
  Returns nil if no current_user is present.

  ## Examples

      actor = current_actor(socket)
      members = Membership.list_members!(actor: actor)
  """
  @spec current_actor(Phoenix.LiveView.Socket.t()) :: Mv.Accounts.User.t() | nil
  def current_actor(socket) do
    socket.assigns[:current_user] || socket.assigns.current_user
  end

  @doc """
  Converts an actor to Ash options list for authorization.
  Returns empty list if actor is nil.

  Delegates to `Mv.Helpers.ash_actor_opts/1` for consistency across the application.

  ## Examples

      opts = ash_actor_opts(actor)
      Ash.read(query, opts)
  """
  @spec ash_actor_opts(Mv.Accounts.User.t() | nil) :: keyword()
  defdelegate ash_actor_opts(actor), to: Mv.Helpers

  @doc """
  Submits an AshPhoenix form with consistent actor handling.

  This wrapper ensures that actor is always passed via `action_opts`
  in a consistent manner across all LiveViews.

  ## Examples

      case submit_form(form, params, actor) do
        {:ok, resource} -> # success
        {:error, form} -> # validation errors
      end
  """
  @spec submit_form(AshPhoenix.Form.t(), map(), Mv.Accounts.User.t() | nil) ::
          {:ok, Ash.Resource.t()} | {:error, AshPhoenix.Form.t()}
  def submit_form(form, params, actor) do
    AshPhoenix.Form.submit(form, params: params, action_opts: ash_actor_opts(actor))
  end
end
