defmodule Raxol.Terminal.Emulator.OptimizedInputProcessor do
  @moduledoc """
  Optimized input processing for the terminal emulator.

  This module provides performance-optimized versions of input processing
  functions with the following improvements:

  - Removed debug IO.puts statements
  - Optimized string concatenation using iolists
  - Reduced function calls and pattern matching
  - Implemented caching for charset commands
  - Minimized cursor position checks
  """

  # Performance.Optimizer lives in main raxol; guarded at runtime
  @compile {:no_warn_undefined,
            [
              Raxol.Performance.Optimizer,
              Raxol.Performance.Profiler,
              Raxol.Performance.Memoization.MemoizationServer
            ]}

  import :erlang, only: [iolist_to_binary: 1]
  alias Raxol.Terminal.{Input.CoreHandler, ScreenBuffer}

  # Cache charset commands for faster lookup
  @charset_commands %{
    "\e)0" => {:g1, :dec_special_graphics},
    "\e(B" => {:g0, :us_ascii},
    "\e*0" => {:g2, :dec_special_graphics},
    "\x0E" => {:gl, :g1},
    "\x0F" => {:gl, :g0},
    "\en" => {:gl, :g2},
    "\eo" => {:gl, :g3},
    "\e~" => {:gr, :g2},
    "\e}" => {:gr, :g1},
    "\e|" => {:gr, :g3}
  }

  @doc """
  Optimized version of process_input that minimizes allocations and function calls.
  """
  def process_input(emulator, input) do
    do_process_input(emulator, input)
  end

  defp do_process_input(emulator, input) do
    # Fast path for common single character input
    case byte_size(input) do
      1 -> process_single_char(emulator, input)
      _ -> process_multi_char(emulator, input)
    end
  end

  defp process_single_char(emulator, <<char>>)
       when char >= 32 and char <= 126 do
    # Fast path for printable ASCII
    CoreHandler.process_terminal_input(emulator, <<char>>)
  end

  defp process_single_char(emulator, input) do
    process_multi_char(emulator, input)
  end

  defp process_multi_char(emulator, input) do
    # Check for mouse events using binary pattern matching
    case input do
      <<0x1B, ?[, ?M, button, x, y, rest::binary>> ->
        handle_mouse_event(emulator, button, x, y, rest)

      _ ->
        handle_non_mouse_input(emulator, input)
    end
  end

  defp handle_mouse_event(emulator, button, x, y, rest) do
    output =
      get_mouse_output(emulator.mode_manager.mouse_report_mode, button, x, y)

    handle_remaining_input(emulator, rest, output)
  end

  defp handle_non_mouse_input(emulator, input) do
    case Map.get(@charset_commands, input) do
      {field, value} ->
        handle_charset_command(emulator, field, value)

      nil ->
        handle_parser_input(emulator, input)
    end
  end

  defp handle_charset_command(emulator, field, value) do
    updated_charset = Map.put(emulator.charset_state, field, value)
    {%{emulator | charset_state: updated_charset}, ""}
  end

  defp handle_parser_input(emulator, input) do
    {updated_emulator, output} =
      CoreHandler.process_terminal_input(emulator, input)

    # Always apply cursor check - the emulator struct doesn't track last_operation
    final_emulator = ensure_cursor_visible_optimized(updated_emulator)

    {final_emulator, output}
  end

  @doc """
  Optimized cursor visibility check that minimizes repeated calculations.
  """
  def ensure_cursor_visible_optimized(emulator) do
    # Early return for autowrap flag
    case emulator do
      %{cursor_handled_by_autowrap: true} ->
        %{emulator | cursor_handled_by_autowrap: false}

      _ ->
        # Use lazy evaluation for expensive operations
        Stream.unfold(emulator, &unfold_cursor_check/1)
        |> Enum.reduce(emulator, fn _, acc -> acc end)
    end
  end

  defp unfold_cursor_check(emu) do
    if cursor_needs_scroll?(emu) do
      handle_cursor_scroll(true, emu)
    else
      handle_cursor_scroll(false, emu)
    end
  end

  defp cursor_needs_scroll?(emulator) do
    # Memoize cursor position within the same operation
    cursor_y = memoize_cursor_y(emulator)
    cursor_y >= get_buffer_height(emulator)
  end

  defp memoize_cursor_y(emulator) do
    case Raxol.Performance.Memoization.MemoizationServer.get_memoized({:get_cursor_y, [emulator]}) do
      nil ->
        result =
          case emulator.cursor do
            pid when is_pid(pid) ->
              {_, y} = Raxol.Terminal.Cursor.Manager.get_position(pid)
              y

            %{row: y} ->
              y

            _ ->
              0
          end

        Raxol.Performance.Memoization.MemoizationServer.memoize(
          {:get_cursor_y, [emulator]},
          result
        )

        result

      cached ->
        cached
    end
  end

  defp get_buffer_height(emulator) do
    # Cache buffer height as it rarely changes
    case Raxol.Performance.Memoization.MemoizationServer.get_memoized(
           {:buffer_height, emulator.active_buffer}
         ) do
      nil ->
        height = ScreenBuffer.get_height(emulator.active_buffer)

        Raxol.Performance.Memoization.MemoizationServer.memoize(
          {:buffer_height, emulator.active_buffer},
          height
        )

        height

      height ->
        height
    end
  end

  defp scroll_once(emulator) do
    # Delegate to scroll operations
    Raxol.Terminal.Emulator.ScrollOperations.scroll_up(emulator, 1)
  end

  @doc """
  Batch process multiple input chunks for better performance.
  """
  def batch_process_inputs(emulator, inputs) when is_list(inputs) do
    # Process inputs in chunks for better performance
    batch_size = 10

    inputs
    |> Enum.chunk_every(batch_size)
    |> Enum.reduce({emulator, []}, fn batch, {emu, all_outputs} ->
      {new_emu, batch_outputs} =
        Enum.reduce(batch, {emu, []}, fn input, {current_emu, outputs} ->
          {updated_emu, output} = process_input(current_emu, input)
          {updated_emu, [output | outputs]}
        end)

      {new_emu, [Enum.reverse(batch_outputs) | all_outputs]}
    end)
    |> then(fn {final_emu, outputs} ->
      {final_emu, outputs |> Enum.reverse() |> List.flatten() |> iolist_to_binary()}
    end)
  end

  @doc """
  Precompile common escape sequences for faster matching.
  """
  def precompile_sequences do
    # Generate optimized pattern matching functions at compile time
    sequences = [
      {~r/^\e\[(\d+)A/, :cursor_up},
      {~r/^\e\[(\d+)B/, :cursor_down},
      {~r/^\e\[(\d+)C/, :cursor_forward},
      {~r/^\e\[(\d+)D/, :cursor_back},
      {~r/^\e\[(\d+);(\d+)H/, :cursor_position},
      {~r/^\e\[2J/, :clear_screen},
      {~r/^\e\[K/, :clear_line}
    ]

    :persistent_term.put(:compiled_sequences, sequences)
  end

  @doc """
  Profile input processing performance.
  """
  def profile_input_processing(emulator, sample_inputs) do
    if Code.ensure_loaded?(Raxol.Performance.Profiler) do
      Raxol.Performance.Profiler.compare(:input_processing,
        old: fn ->
          Enum.each(sample_inputs, fn input ->
            Raxol.Terminal.Emulator.process_input(emulator, input)
          end)
        end,
        new: fn ->
          Enum.each(sample_inputs, fn input ->
            process_input(emulator, input)
          end)
        end
      )
    else
      :ok
    end
  end

  # Helper functions for pattern matching instead of if statements
  defp get_mouse_output(:none, _button, _x, _y), do: []
  defp get_mouse_output(_mode, button, x, y), do: ["\e[M", button, x, y]

  defp handle_remaining_input(emulator, <<>>, output) do
    {emulator, iolist_to_binary(output)}
  end

  defp handle_remaining_input(emulator, rest, output) do
    {final_emulator, remaining_output} = process_input(emulator, rest)
    {final_emulator, iolist_to_binary([output | remaining_output])}
  end

  defp handle_cursor_scroll(true, emu) do
    scrolled_emu = scroll_once(emu)
    {scrolled_emu, emu}
  end

  defp handle_cursor_scroll(false, emu), do: {nil, emu}
end
