defmodule DaProductApp.Events.EventRegistry do @moduledoc """ Registry for managing event listeners and their lifecycle. Provides utility functions for: - Auto-discovery and registration of event listeners - Listener lifecycle management - Configuration-based listener registration """ require Logger alias DaProductApp.Events.EventDispatcher @doc """ Auto-register all event listeners found in the application. Scans for modules implementing EventListenerBehaviour and registers them. """ def auto_register_listeners do Logger.info("Auto-registering event listeners") # Get all modules in the application {:ok, modules} = :application.get_key(:da_product_app, :modules) listener_modules = modules |> Enum.filter(&implements_event_listener?/1) |> Enum.each(&EventDispatcher.register_listener/1) Logger.info("Auto-registered #{length(listener_modules)} event listeners") :ok rescue exception -> Logger.error("Failed to auto-register listeners: #{inspect(exception)}") {:error, exception} end @doc """ Register listeners from configuration. ## Configuration Example ```elixir config :da_product_app, :event_listeners, [ DaProductApp.Acquirer.YSP.TransactionEventListener, DaProductApp.Events.Listeners.ValidationListener, DaProductApp.Events.Listeners.LoggingListener ] ``` """ def register_configured_listeners do Logger.info("Registering configured event listeners") listeners = Application.get_env(:da_product_app, :event_listeners, []) results = Enum.map(listeners, fn listener_module -> case EventDispatcher.register_listener(listener_module) do :ok -> Logger.info("Registered configured listener: #{inspect(listener_module)}") {:ok, listener_module} {:error, reason} -> Logger.error("Failed to register configured listener #{inspect(listener_module)}: #{inspect(reason)}") {:error, listener_module, reason} end end) successes = Enum.count(results, fn {status, _} -> status == :ok end) failures = Enum.count(results, fn {status, _} -> status == :error end) Logger.info("Registered #{successes} configured listeners, #{failures} failed") if failures > 0 do {:error, results} else {:ok, results} end end @doc """ Register specific listener modules. ## Parameters - `listener_modules`: List of modules to register """ def register_listeners(listener_modules) when is_list(listener_modules) do Logger.info("Registering specific event listeners: #{inspect(listener_modules)}") results = Enum.map(listener_modules, &EventDispatcher.register_listener/1) successes = Enum.count(results, & &1 == :ok) failures = length(results) - successes Logger.info("Registered #{successes} listeners, #{failures} failed") if failures > 0 do {:error, results} else {:ok, results} end end ## Private Functions defp implements_event_listener?(module) do try do # Check if module is loaded and implements the behaviour Code.ensure_loaded?(module) and function_exported?(module, :subscribed_events, 0) and function_exported?(module, :handle_event, 2) and has_behaviour?(module, DaProductApp.Events.EventListenerBehaviour) rescue _ -> false end end defp has_behaviour?(module, behaviour) do try do behaviours = module.module_info(:attributes) |> Keyword.get(:behaviour, []) behaviour in behaviours rescue _ -> false end end end