defmodule Raxol.Terminal.EventProcessor do
  @moduledoc """
  Optimized event processing pipeline for terminal events.

  This module provides high-performance event processing with:
  - Batch event processing for improved throughput
  - Event priority handling
  - Optimized memory usage with pre-compiled handlers
  - Event filtering and debouncing
  - Performance monitoring integration
  """

  require Raxol.Core.Runtime.Log

  alias Raxol.Core.Events.Event
  alias Raxol.Terminal.Emulator
  alias Raxol.Terminal.Events.Handler

  # Pre-compiled event handlers for maximum performance
  @event_handlers %{
    window: &Handler.handle_window_event/2,
    mode: &Handler.handle_mode_event/2,
    focus: &Handler.handle_focus_event/2,
    clipboard: &Handler.handle_clipboard_event/2,
    selection: &Handler.handle_selection_event/2,
    paste: &Handler.handle_paste_event/2,
    cursor: &Handler.handle_cursor_event/2,
    scroll: &Handler.handle_scroll_event/2,
    keyboard: &Handler.handle_keyboard_event/2,
    mouse: &Handler.handle_mouse_event/2
  }

  # Event priority levels for processing order
  @event_priorities %{
    # Highest priority - user input
    keyboard: 1,
    # Highest priority - user input
    mouse: 1,
    # High priority - visual feedback
    cursor: 2,
    # High priority - visual feedback
    scroll: 2,
    # Medium priority
    selection: 3,
    # Medium priority
    paste: 3,
    # Medium priority
    clipboard: 3,
    # Low priority
    focus: 4,
    # Low priority
    window: 4,
    # Lowest priority
    mode: 5
  }

  # Debounce intervals (in milliseconds) for event types
  @debounce_intervals %{
    # ~60fps for smooth scrolling
    scroll: 16,
    # Avoid excessive redraws during window resize
    resize: 100,
    # ~120fps for responsive mouse tracking
    mouse: 8
  }

  @doc """
  Processes a single terminal event with optimized performance.

  ## Parameters
    * `event` - The event to process
    * `emulator` - The current terminal emulator state

  ## Returns
    * `{updated_emulator, output}` - The updated emulator state and any output
  """
  @spec process_event(Event.t(), Emulator.t()) :: {Emulator.t(), any()}
  def process_event(%Event{type: type, data: data} = event, emulator) do
    # Fast path for known event types
    case Map.fetch(@event_handlers, type) do
      {:ok, handler} ->
        start_time = System.monotonic_time(:microsecond)
        result = handler.(data, emulator)
        end_time = System.monotonic_time(:microsecond)

        # Record performance metrics for monitoring
        record_event_processing_time(type, end_time - start_time)

        result

      :error ->
        handle_unknown_event(event, emulator)
    end
  end

  @doc """
  Processes multiple events in batch for improved performance.

  ## Parameters
    * `events` - List of events to process
    * `emulator` - The current terminal emulator state

  ## Returns
    * `{updated_emulator, outputs}` - The updated emulator state and list of outputs
  """
  @spec process_events_batch([Event.t()], Emulator.t()) ::
          {Emulator.t(), [any()]}
  def process_events_batch([], emulator), do: {emulator, []}

  def process_events_batch(events, emulator) when is_list(events) do
    # Sort events by priority for optimal processing order
    sorted_events = sort_events_by_priority(events)

    # Filter out events that should be debounced
    filtered_events = apply_debouncing(sorted_events)

    # Process events with accumulated state
    {final_emulator, outputs} =
      Enum.reduce(filtered_events, {emulator, []}, fn event,
                                                      {acc_emulator,
                                                       acc_outputs} ->
        {updated_emulator, output} = process_event(event, acc_emulator)
        {updated_emulator, [output | acc_outputs]}
      end)

    {final_emulator, Enum.reverse(outputs)}
  end

  @doc """
  Processes high-priority events immediately, queues others.

  ## Parameters
    * `event` - The event to process
    * `emulator` - The current terminal emulator state
    * `options` - Processing options

  ## Returns
    * `{:immediate, updated_emulator, output}` - Processed immediately
    * `{:queued, emulator}` - Queued for later processing
  """
  @spec process_event_with_priority(Event.t(), Emulator.t(), keyword()) ::
          {:immediate, Emulator.t(), any()} | {:queued, Emulator.t()}
  def process_event_with_priority(
        %Event{type: type} = event,
        emulator,
        options \\ []
      ) do
    priority = Map.get(@event_priorities, type, 5)
    immediate_threshold = Keyword.get(options, :immediate_threshold, 2)

    case priority <= immediate_threshold do
      true ->
        {updated_emulator, output} = process_event(event, emulator)
        {:immediate, updated_emulator, output}

      false ->
        # Queue for batch processing
        {:queued, emulator}
    end
  end

  @doc """
  Optimized event filtering to reduce processing overhead.
  """
  @spec filter_redundant_events([Event.t()]) :: [Event.t()]
  def filter_redundant_events(events) when is_list(events) do
    # Group events by type and keep only the most recent for certain types
    events
    |> Enum.group_by(fn %Event{type: type} -> type end)
    |> Enum.flat_map(fn {type, type_events} ->
      case should_deduplicate_event_type(type) do
        # Keep only the latest
        true -> [List.last(type_events)]
        # Keep all events
        false -> type_events
      end
    end)
    |> Enum.sort_by(fn %Event{timestamp: timestamp} -> timestamp end)
  end

  # Private helper functions

  defp handle_unknown_event(%Event{type: type, data: data}, emulator) do
    Raxol.Core.Runtime.Log.warning_with_context(
      "Unknown terminal event type: #{inspect(type)} with data: #{inspect(data)}",
      %{event_type: type, data_size: byte_size(inspect(data))}
    )

    {emulator, nil}
  end

  defp sort_events_by_priority(events) do
    Enum.sort_by(events, fn %Event{type: type} ->
      Map.get(@event_priorities, type, 5)
    end)
  end

  defp apply_debouncing(events) do
    # Simple debouncing implementation - in production, this would be more sophisticated
    events
    |> Enum.group_by(fn %Event{type: type} -> type end)
    |> Enum.flat_map(fn {type, type_events} ->
      case Map.get(@debounce_intervals, type) do
        nil -> type_events
        _interval -> debounce_events_of_type(type_events)
      end
    end)
    |> Enum.sort_by(fn %Event{timestamp: timestamp} -> timestamp end)
  end

  defp debounce_events_of_type(events) when length(events) <= 1, do: events

  defp debounce_events_of_type(events) do
    # For demonstration - keep first and last event, drop middle ones
    case events do
      [first | rest] ->
        last = List.last(rest)

        case first == last do
          true -> [first]
          false -> [first, last]
        end

      [] ->
        []
    end
  end

  defp should_deduplicate_event_type(type) do
    # Event types that can be deduplicated (keep only latest)
    type in [:scroll, :cursor, :focus]
  end

  defp record_event_processing_time(event_type, duration_microseconds) do
    # Send metrics to performance monitoring system
    case Application.get_env(:raxol, :enable_performance_metrics, false) do
      true ->
        :telemetry.execute(
          [:raxol, :terminal, :event_processing],
          %{duration: duration_microseconds},
          %{event_type: event_type}
        )

      false ->
        :ok
    end
  end
end
