defmodule Raxol.Terminal.Input.InputHandler do
  @moduledoc """
  Handles input processing for the terminal emulator.

  This module manages keyboard input, mouse events, input history,
  and modifier key states.
  """

  defstruct buffer: "",
            mode: :normal,
            mouse_enabled: false,
            mouse_buttons: MapSet.new(),
            mouse_position: {0, 0},
            input_history: [],
            history_index: nil,
            modifier_state: %{
              ctrl: false,
              alt: false,
              shift: false,
              meta: false
            }

  @type t :: %__MODULE__{}

  @doc """
  Creates a new input handler with default values.
  """
  def new do
    %__MODULE__{}
  end

  @doc """
  Processes regular keyboard input.
  """
  def process_keyboard(%__MODULE__{} = handler, key) when is_binary(key) do
    %{handler | buffer: handler.buffer <> key}
  end

  @doc """
  Processes special keys like arrow keys, function keys, etc.
  """
  def process_special_key(%__MODULE__{} = handler, key) do
    sequence = Raxol.Terminal.Input.SpecialKeys.atom_to_escape_sequence(key)
    %{handler | buffer: sequence}
  end

  @doc """
  Updates modifier key state.
  """
  def update_modifier(%__MODULE__{} = handler, modifier, state) do
    modifier_key =
      case modifier do
        "Control" -> :ctrl
        "Alt" -> :alt
        "Shift" -> :shift
        "Meta" -> :meta
        _ -> :ctrl
      end

    modifier_state = Map.put(handler.modifier_state, modifier_key, state)
    %{handler | modifier_state: modifier_state}
  end

  @doc """
  Processes key with current modifier state.
  """
  def process_key_with_modifiers(%__MODULE__{} = handler, key) do
    sequence =
      Raxol.Terminal.Input.SpecialKeys.key_with_modifiers_to_escape_sequence(
        handler.modifier_state,
        key
      )

    %{handler | buffer: sequence}
  end

  @doc """
  Processes mouse events.
  """
  def process_mouse(%__MODULE__{} = handler, {action, button, x, y}) do
    handle_mouse_event(handler.mouse_enabled, handler, action, button, x, y)
  end

  defp handle_mouse_event(false, handler, _action, _button, _x, _y), do: handler

  defp handle_mouse_event(true, handler, action, button, x, y) do
    action_code =
      case action do
        :press -> "M"
        :release -> "m"
        :drag -> "M"
        _ -> ""
      end

    sequence = "\e[<#{button};#{x + 1};#{y + 1}#{action_code}"

    mouse_buttons =
      case action do
        :press -> MapSet.put(handler.mouse_buttons, button)
        :release -> MapSet.delete(handler.mouse_buttons, button)
        _ -> handler.mouse_buttons
      end

    %{
      handler
      | buffer: sequence,
        mouse_position: {x, y},
        mouse_buttons: mouse_buttons
    }
  end

  @doc """
  Sets mouse enabled state.
  """
  def set_mouse_enabled(%__MODULE__{} = handler, enabled) do
    %{handler | mouse_enabled: enabled}
  end

  @doc """
  Sets input mode.
  """
  def set_mode(%__MODULE__{} = handler, mode) do
    %{handler | mode: mode}
  end

  @doc """
  Gets current input mode.
  """
  def get_mode(%__MODULE__{} = handler) do
    handler.mode
  end

  @doc """
  Adds current buffer to history if not empty.
  """
  def add_to_history(%__MODULE__{} = handler) do
    add_buffer_to_history(handler.buffer, handler)
  end

  defp add_buffer_to_history("", handler), do: handler

  defp add_buffer_to_history(buffer, handler) do
    history = [buffer | handler.input_history]
    %{handler | input_history: history, history_index: nil, buffer: ""}
  end

  @doc """
  Gets history entry at specified index.
  """
  def get_history_entry(%__MODULE__{} = handler, index) do
    history_length = length(handler.input_history)

    get_entry_if_valid_index(
      index >= 0 and index < history_length,
      handler,
      index
    )
  end

  defp get_entry_if_valid_index(false, handler, _index), do: handler

  defp get_entry_if_valid_index(true, handler, index) do
    entry = Enum.at(handler.input_history, index)
    %{handler | buffer: entry, history_index: index}
  end

  @doc """
  Moves to next (newer) history entry.
  """
  def next_history_entry(%__MODULE__{} = handler) do
    handle_next_history(handler.history_index, handler.input_history, handler)
  end

  defp handle_next_history(nil, _history, handler),
    do: {handler, handler.buffer}

  defp handle_next_history(_index, [], handler), do: {handler, handler.buffer}

  defp handle_next_history(index, history, handler) do
    new_index = max(index - 1, 0)
    entry = Enum.at(history, new_index)
    new_handler = %{handler | buffer: entry, history_index: new_index}
    {new_handler, entry}
  end

  @doc """
  Moves to previous (older) history entry.
  """
  def previous_history_entry(%__MODULE__{} = handler) do
    history_length = length(handler.input_history)
    handle_previous_history(handler.history_index, history_length, handler)
  end

  defp handle_previous_history(nil, history_length, handler)
       when history_length > 0 do
    entry = hd(handler.input_history)
    new_handler = %{handler | buffer: entry, history_index: 0}
    {new_handler, entry}
  end

  defp handle_previous_history(nil, _history_length, handler),
    do: {handler, handler.buffer}

  defp handle_previous_history(index, history_length, handler)
       when index < history_length - 1 do
    new_index = index + 1
    entry = Enum.at(handler.input_history, new_index)
    new_handler = %{handler | buffer: entry, history_index: new_index}
    {new_handler, entry}
  end

  defp handle_previous_history(_index, _history_length, handler),
    do: {handler, handler.buffer}

  @doc """
  Clears the input buffer.
  """
  def clear_buffer(%__MODULE__{} = handler) do
    %{handler | buffer: ""}
  end

  @doc """
  Checks if buffer is empty.
  """
  def buffer_empty?(%__MODULE__{} = handler) do
    handler.buffer == ""
  end

  @doc """
  Gets buffer contents.
  """
  def get_buffer_contents(%__MODULE__{} = handler) do
    handler.buffer
  end

  @doc """
  Handles printable character input for the terminal emulator.
  """
  def handle_printable_character(
        emulator,
        char_codepoint,
        _params,
        _single_shift
      ) do
    # Processing printable character

    # Use the CharacterProcessor to handle the printable character
    # This will write the character to the buffer and update cursor position
    updated_emulator =
      Raxol.Terminal.Input.CharacterProcessor.process_printable_character(
        emulator,
        char_codepoint
      )

    {updated_emulator, nil}
  end
end
