defmodule Raxol.Plugins.Manager.Cells do
  @moduledoc """
  Handles cell processing functionality.
  Provides functions for processing and transforming cells through plugins.
  """

  require Raxol.Core.Runtime.Log

  alias Raxol.Plugins.CellProcessor
  alias Raxol.Plugins.Manager.Core

  @doc """
  Allows plugins to process or replace cells generated by the renderer.
  Delegates processing to `Raxol.Plugins.CellProcessor`.

  Returns `{:ok, updated_manager, processed_cells, collected_commands}`.
  """
  def handle_cells(%Core{} = manager, cells, emulator_state)
      when is_list(cells) do
    # Delegate to the CellProcessor module
    CellProcessor.process(manager, cells, emulator_state)
  end

  @doc """
  Processes a single cell through all enabled plugins.
  Returns {:ok, updated_manager, processed_cell} or {:error, reason}.
  """
  def process_cell(%Core{} = manager, cell, emulator_state) do
    Enum.reduce_while(
      manager.plugins,
      {:ok, manager, cell},
      &process_plugin_cell(&1, &2, emulator_state)
    )
  end

  defp process_plugin_cell(
         {_name, plugin},
         {:ok, acc_manager, acc_cell},
         emulator_state
       ) do
    handle_plugin_enabled_cell(
      plugin.enabled,
      plugin,
      acc_cell,
      acc_manager,
      emulator_state
    )
  end

  # Helper functions to eliminate if statements

  defp handle_plugin_enabled_cell(
         false,
         _plugin,
         acc_cell,
         acc_manager,
         _emulator_state
       ) do
    {:cont, {:ok, acc_manager, acc_cell}}
  end

  defp handle_plugin_enabled_cell(
         true,
         plugin,
         acc_cell,
         acc_manager,
         emulator_state
       ) do
    module = plugin.__struct__

    handle_function_exported_cell(
      function_exported?(module, :process_cell, 3),
      module,
      plugin,
      acc_cell,
      acc_manager,
      emulator_state
    )
  end

  defp handle_function_exported_cell(
         false,
         _module,
         _plugin,
         acc_cell,
         acc_manager,
         _emulator_state
       ) do
    {:cont, {:ok, acc_manager, acc_cell}}
  end

  defp handle_function_exported_cell(
         true,
         module,
         plugin,
         acc_cell,
         acc_manager,
         emulator_state
       ) do
    handle_plugin_cell(module, plugin, acc_cell, acc_manager, emulator_state)
  end

  defp handle_plugin_cell(module, plugin, acc_cell, acc_manager, emulator_state) do
    case module.process_cell(plugin, acc_cell, emulator_state) do
      {:ok, updated_plugin, processed_cell} ->
        updated_manager =
          Core.update_plugins(
            acc_manager,
            Map.put(acc_manager.plugins, plugin.name, updated_plugin)
          )

        {:cont, {:ok, updated_manager, processed_cell}}

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

  @doc """
  Collects cell-related commands from all enabled plugins.
  Returns {:ok, updated_manager, commands}.
  """
  def collect_cell_commands(%Core{} = manager) do
    Enum.reduce(manager.plugins, {:ok, manager, []}, &collect_plugin_commands/2)
  end

  defp collect_plugin_commands(
         {_name, plugin},
         {:ok, acc_manager, acc_commands}
       ) do
    handle_plugin_enabled_commands(
      plugin.enabled,
      plugin,
      acc_manager,
      acc_commands
    )
  end

  defp handle_plugin_enabled_commands(false, _plugin, acc_manager, acc_commands) do
    {:ok, acc_manager, acc_commands}
  end

  defp handle_plugin_enabled_commands(true, plugin, acc_manager, acc_commands) do
    module = plugin.__struct__

    handle_function_exported_commands(
      function_exported?(module, :get_cell_commands, 1),
      module,
      plugin,
      acc_manager,
      acc_commands
    )
  end

  defp handle_function_exported_commands(
         false,
         _module,
         _plugin,
         acc_manager,
         acc_commands
       ) do
    {:ok, acc_manager, acc_commands}
  end

  defp handle_function_exported_commands(
         true,
         module,
         plugin,
         acc_manager,
         acc_commands
       ) do
    handle_plugin_commands(module, plugin, acc_manager, acc_commands)
  end

  defp handle_plugin_commands(module, plugin, acc_manager, acc_commands) do
    case module.get_cell_commands(plugin) do
      {:ok, updated_plugin, commands} ->
        updated_manager =
          Core.update_plugins(
            acc_manager,
            Map.put(acc_manager.plugins, plugin.name, updated_plugin)
          )

        {:ok, updated_manager, commands ++ acc_commands}

      {:ok, updated_plugin} ->
        updated_manager =
          Core.update_plugins(
            acc_manager,
            Map.put(acc_manager.plugins, plugin.name, updated_plugin)
          )

        {:ok, updated_manager, acc_commands}

      commands when is_list(commands) ->
        {:ok, acc_manager, commands ++ acc_commands}
    end
  end
end
