defmodule Raxol.Core.Runtime.Events.Handler do
  @moduledoc """
  Manages event handlers registration and execution in the Raxol system.

  This module is responsible for:
  * Registering event handlers for specific event types
  * Executing handlers when events occur
  * Managing the priority and order of handlers
  """

  require Raxol.Core.Runtime.Log
  alias Raxol.Core.Runtime.ProcessStore

  @doc """
  Registers a new event handler for the specified event types.
  """
  def register_handler(handler_id, event_types, handler_fun, options \\ []) do
    priority = Keyword.get(options, :priority, 100)
    filter = Keyword.get(options, :filter, fn _event -> true end)

    handler = %{
      id: handler_id,
      event_types: List.wrap(event_types),
      handler_fun: handler_fun,
      priority: priority,
      filter: filter
    }

    _ = put_handler(handler_id, handler)

    {:ok, handler_id}
  end

  @doc """
  Unregisters an event handler.

  ## Parameters
  - `handler_id`: ID of the handler to remove
  """
  def unregister_handler(handler_id) do
    case get_handler(handler_id) do
      nil ->
        {:error, :not_found}

      _handler ->
        _ = remove_handler(handler_id)
        :ok
    end
  end

  @doc """
  Executes all registered handlers for the given event.
  Handlers are executed in priority order (lowest to highest).
  Each handler can transform the event for the next handler.
  """
  def execute_handlers(event, state) do
    handlers = get_relevant_handlers(event)
    execute_handlers_in_order(handlers, event, state)
  end

  @spec get_relevant_handlers(any()) :: any() | nil
  defp get_relevant_handlers(event) do
    get_all_handlers()
    |> Enum.filter(fn handler ->
      event.type in handler.event_types and handler.filter.(event)
    end)
    |> Enum.sort_by(fn handler -> handler.priority end)
  end

  @spec execute_handlers_in_order(any(), any(), map()) :: any()
  defp execute_handlers_in_order(handlers, event, state) do
    case Raxol.Core.ErrorHandling.safe_call(fn ->
           Enum.reduce_while(
             handlers,
             {event, state},
             &execute_single_handler/2
           )
           |> case do
             {updated_event, updated_state} ->
               {:ok, updated_event, updated_state}

             {:error, reason, error_state} ->
               {:error, reason, error_state}
           end
         end) do
      {:ok, result} ->
        result

      {:error, error} ->
        log_handler_error(error, event, state, nil)
        {:error, {:handler_error, error}, state}
    end
  end

  @spec execute_single_handler(any(), any()) :: any()
  defp execute_single_handler(handler, {current_event, current_state}) do
    case handler.handler_fun.(current_event, current_state) do
      {:ok, new_event, new_state} ->
        {:cont, {new_event, new_state}}

      {:stop, new_event, new_state} ->
        {:halt, {new_event, new_state}}

      {:error, reason} ->
        Raxol.Core.Runtime.Log.error(
          "Handler error",
          %{
            module: __MODULE__,
            event: current_event,
            state: current_state,
            reason: reason
          }
        )

        {:halt, {:error, reason, current_state}}
    end
  end

  @spec log_handler_error(any(), any(), map(), any()) :: any()
  defp log_handler_error(error, event, state, stacktrace) do
    case Raxol.Core.ErrorHandling.safe_call(fn ->
           Raxol.Core.Runtime.Log.error_with_stacktrace(
             "Error executing handlers",
             error,
             stacktrace,
             %{module: __MODULE__, event: event, state: state}
           )
         end) do
      {:ok, _} ->
        :ok

      {:error, e} ->
        Raxol.Core.Runtime.Log.error(
          "Failed to log handler error: #{inspect(e)}",
          %{module: __MODULE__, event: event, state: state}
        )
    end
  end

  @spec put_handler(String.t() | integer(), any()) :: any()
  defp put_handler(id, handler) do
    # Create a simple module and function name for EventManager registration
    # Since EventManager expects (event_type, module, function), we'll register
    # a generic handler that can invoke the function-based handlers
    Enum.each(handler.event_types, fn event_type ->
      Raxol.Core.Events.EventManager.register_handler(
        event_type,
        __MODULE__,
        :execute_dynamic_handler
      )
    end)

    # Store the handler config for retrieval
    _ = ProcessStore.put({:handler_config, id}, handler)
    {:ok, id}
  end

  @doc """
  Executes a dynamically registered handler.
  This is called by EventManager and looks up the actual handler function.
  """
  def execute_dynamic_handler(event) do
    # Get all stored handler configs and find ones that match this event type
    handlers =
      get_all_stored_handlers()
      |> Enum.filter(fn {_id, handler} ->
        Enum.member?(handler.event_types, event.type)
      end)
      |> Enum.sort_by(fn {_id, handler} -> handler.priority end)

    # Execute handlers in priority order
    initial_state = %{count: 0}

    Enum.reduce_while(handlers, {event, initial_state}, fn {_id, handler},
                                                           {current_event,
                                                            current_state} ->
      if handler.filter.(current_event) do
        try do
          case handler.handler_fun.(current_event, current_state) do
            {:ok, updated_event, updated_state} ->
              {:cont, {updated_event, updated_state}}

            {:error, reason, updated_state} ->
              {:halt, {:error, reason, updated_state}}

            {:stop, updated_event, updated_state} ->
              {:halt, {updated_event, updated_state}}
          end
        rescue
          error ->
            {:halt, {:error, {:handler_error, error}, current_state}}
        end
      else
        {:cont, {current_event, current_state}}
      end
    end)
  end

  defp get_all_stored_handlers do
    # Get all handler configs from store
    ProcessStore.get_all()
    |> Enum.filter(fn
      {{key, _id}, _handler} when key == :handler_config -> true
      _ -> false
    end)
    |> Enum.map(fn {{_key, id}, handler} -> {id, handler} end)
  end

  @spec get_handler(String.t() | integer()) :: any() | nil
  defp get_handler(id) do
    case ProcessStore.get({:handler_config, id}) do
      nil -> nil
      handler -> {id, handler}
    end
  end

  @spec remove_handler(String.t() | integer()) :: any()
  defp remove_handler(id) do
    case ProcessStore.get({:handler_config, id}) do
      nil ->
        {:error, :not_found}

      handler ->
        # Remove from store
        _ = ProcessStore.delete({:handler_config, id})

        # Try to unregister from EventManager (though this is complex with the current design)
        Enum.each(handler.event_types, fn event_type ->
          try do
            Raxol.Core.Events.EventManager.unregister_handler(
              event_type,
              __MODULE__,
              :execute_dynamic_handler
            )
          rescue
            # Ignore if already unregistered
            _ -> :ok
          end
        end)

        :ok
    end
  end

  defp get_all_handlers do
    get_all_stored_handlers()
    |> Enum.map(fn {_id, handler} -> handler end)
  end
end
