defmodule Raxol.Terminal.Events.Handler do
  @moduledoc """
  Handles terminal events and dispatches them to appropriate handlers.
  """

  require Raxol.Core.Runtime.Log
  alias Raxol.Terminal.ANSI.Window.Manipulation, as: WindowManipulation

  @doc """
  Handles window-related events.
  """
  def handle_window_event(emulator_state, event) do
    case event do
      {:resize, w, h} ->
        handle_resize(emulator_state, w, h)

      {:title, title} ->
        handle_title_change(emulator_state, title)

      {:icon_name, name} ->
        handle_icon_name_change(emulator_state, name)

      _ ->
        {:error, "Unknown window event: #{inspect(event)}"}
    end
  end

  @doc """
  Handles mode change events.
  """
  def handle_mode_event(emulator_state, event) do
    case event do
      {:change, new_mode} ->
        handle_mode_change(emulator_state, new_mode)

      _ ->
        {:error, "Unknown mode event: #{inspect(event)}"}
    end
  end

  @doc """
  Handles mouse events.
  """
  def handle_mouse_event(emulator_state, event) do
    case event do
      {:click, button, x, y} ->
        handle_mouse_click(emulator_state, button, x, y)

      {:drag, button, x, y} ->
        handle_mouse_drag(emulator_state, button, x, y)

      {:release, button, x, y} ->
        handle_mouse_release(emulator_state, button, x, y)

      _ ->
        {:error, "Unknown mouse event: #{inspect(event)}"}
    end
  end

  @doc """
  Handles keyboard events.
  """
  def handle_keyboard_event(emulator_state, event) do
    case event do
      {:press, key} ->
        handle_key_press(emulator_state, key)

      {:release, key} ->
        handle_key_release(emulator_state, key)

      _ ->
        {:error, "Unknown keyboard event: #{inspect(event)}"}
    end
  end

  @doc """
  Handles focus events.
  """
  def handle_focus_event(emulator_state, event) do
    case event do
      {:gain} ->
        handle_focus_gain(emulator_state)

      {:loss} ->
        handle_focus_loss(emulator_state)

      _ ->
        {:error, "Unknown focus event: #{inspect(event)}"}
    end
  end

  @doc """
  Generic event handler that dispatches to appropriate handlers.
  """
  def handle_event(emulator_state, event) do
    case event do
      {:window, window_event} ->
        handle_window_event(emulator_state, window_event)

      {:mode, mode_event} ->
        handle_mode_event(emulator_state, mode_event)

      {:mouse, mouse_event} ->
        handle_mouse_event(emulator_state, mouse_event)

      {:keyboard, keyboard_event} ->
        handle_keyboard_event(emulator_state, keyboard_event)

      {:focus, focus_event} ->
        handle_focus_event(emulator_state, focus_event)

      _ ->
        {:error, "Unknown event type: #{inspect(event)}"}
    end
  end

  # Private helper functions

  defp handle_resize(emulator_state, w, h) do
    # Update terminal dimensions
    updated_state = %{emulator_state | width: w, height: h}

    # Clear screen and reset cursor position
    commands = [
      WindowManipulation.clear_screen(),
      WindowManipulation.move_cursor(1, 1)
    ]

    {:ok, updated_state, commands}
  end

  defp handle_title_change(emulator_state, title) do
    # Update window title
    updated_state = %{emulator_state | title: title}

    # Send title change command
    commands = [WindowManipulation.set_title(title)]

    {:ok, updated_state, commands}
  end

  defp handle_icon_name_change(emulator_state, name) do
    # Update icon name
    updated_state = %{emulator_state | icon_name: name}

    # Send icon name change command
    commands = [WindowManipulation.set_icon_name(name)]

    {:ok, updated_state, commands}
  end

  defp handle_mode_change(emulator_state, new_mode) do
    # Update terminal mode
    updated_state = %{emulator_state | mode: new_mode}

    # Send mode change command
    commands = [WindowManipulation.set_mode(new_mode)]

    {:ok, updated_state, commands}
  end

  defp handle_mouse_click(emulator_state, button, x, y) do
    # Handle mouse click
    commands = [WindowManipulation.mouse_click(button, x, y)]

    {:ok, emulator_state, commands}
  end

  defp handle_mouse_drag(emulator_state, button, x, y) do
    # Handle mouse drag
    commands = [WindowManipulation.mouse_drag(button, x, y)]

    {:ok, emulator_state, commands}
  end

  defp handle_mouse_release(emulator_state, button, x, y) do
    # Handle mouse release
    commands = [WindowManipulation.mouse_release(button, x, y)]

    {:ok, emulator_state, commands}
  end

  defp handle_key_press(emulator_state, key) do
    # Handle key press
    commands = [WindowManipulation.key_press(key)]

    {:ok, emulator_state, commands}
  end

  defp handle_key_release(emulator_state, key) do
    # Handle key release
    commands = [WindowManipulation.key_release(key)]

    {:ok, emulator_state, commands}
  end

  defp handle_focus_gain(emulator_state) do
    # Handle focus gain
    commands = [WindowManipulation.focus_gain()]

    {:ok, emulator_state, commands}
  end

  defp handle_focus_loss(emulator_state) do
    # Handle focus loss
    commands = [WindowManipulation.focus_loss()]

    {:ok, emulator_state, commands}
  end

  # === Additional Event Handlers ===

  @doc """
  Handles selection events.
  """
  @spec handle_selection_event(any(), any()) ::
          {:ok, any()} | {:error, String.t()}
  def handle_selection_event(emulator_state, event) do
    case event do
      %{start_pos: start_pos, end_pos: end_pos, text: text} ->
        # Update selection in the emulator state
        updated_state =
          update_selection_in_state(emulator_state, start_pos, end_pos, text)

        {:ok, updated_state}

      _ ->
        {:error, "Invalid selection event: #{inspect(event)}"}
    end
  end

  defp update_selection_in_state(emulator_state, start_pos, end_pos, _text) do
    # Update the screen buffer with the new selection
    updated_buffer =
      Raxol.Terminal.Buffer.Selection.start(
        emulator_state.screen_buffer,
        elem(start_pos, 0),
        elem(start_pos, 1)
      )

    updated_buffer =
      Raxol.Terminal.Buffer.Selection.update(
        updated_buffer,
        elem(end_pos, 0),
        elem(end_pos, 1)
      )

    %{emulator_state | screen_buffer: updated_buffer}
  end

  @doc """
  Handles scroll events.
  """
  @spec handle_scroll_event(any(), any()) :: {:ok, any()} | {:error, String.t()}
  def handle_scroll_event(emulator_state, event) do
    case event do
      %{direction: direction, delta: delta, position: position} ->
        # Update scroll position in the emulator state
        updated_state =
          update_scroll_in_state(emulator_state, direction, delta, position)

        {:ok, updated_state}

      _ ->
        {:error, "Invalid scroll event: #{inspect(event)}"}
    end
  end

  defp update_scroll_in_state(emulator_state, direction, delta, _position) do
    # Apply scroll operation based on direction and delta
    case direction do
      :vertical ->
        case delta > 0 do
          true ->
            # Scroll down
            Raxol.Terminal.Commands.Screen.scroll_down(emulator_state, delta)

          false ->
            # Scroll up
            Raxol.Terminal.Commands.Screen.scroll_up(emulator_state, abs(delta))
        end

      :horizontal ->
        # Horizontal scrolling not yet implemented, return unchanged state
        emulator_state

      _ ->
        emulator_state
    end
  end

  @doc """
  Handles paste events.
  """
  @spec handle_paste_event(any(), any()) :: {:ok, any()} | {:error, String.t()}
  def handle_paste_event(emulator_state, event) do
    case event do
      %{text: text} ->
        # Update the screen buffer with the pasted text
        updated_buffer =
          Raxol.Terminal.Buffer.Paste.paste(emulator_state.screen_buffer, text)

        {:ok, %{emulator_state | screen_buffer: updated_buffer}}

      _ ->
        {:error, "Invalid paste event: #{inspect(event)}"}
    end
  end

  @doc """
  Handles cursor events.
  """
  @spec handle_cursor_event(any(), any()) :: {:ok, any()} | {:error, String.t()}
  def handle_cursor_event(emulator_state, event) do
    case event do
      %{visible: visible, style: style, blink: blink, position: position} ->
        # Update cursor properties in the emulator state
        updated_state =
          update_cursor_in_state(
            emulator_state,
            visible,
            style,
            blink,
            position
          )

        {:ok, updated_state}

      _ ->
        {:error, "Invalid cursor event: #{inspect(event)}"}
    end
  end

  defp update_cursor_in_state(emulator_state, visible, style, blink, position) do
    cursor = emulator_state.cursor
    cursor = Raxol.Terminal.Cursor.Manager.set_visibility(cursor, visible)
    cursor = Raxol.Terminal.Cursor.Manager.set_style(cursor, style)
    cursor = Raxol.Terminal.Cursor.Manager.set_blink(cursor, blink)
    cursor = Raxol.Terminal.Cursor.Manager.set_position(cursor, position)

    %{emulator_state | cursor: cursor}
  end

  @doc """
  Handles clipboard events.
  """
  @spec handle_clipboard_event(any(), any()) ::
          {:ok, any()} | {:error, String.t()}
  def handle_clipboard_event(emulator_state, event) do
    case event do
      %{op: op, content: content} ->
        # Handle clipboard operations (copy, cut, etc.)
        updated_state = handle_clipboard_operation(emulator_state, op, content)
        {:ok, updated_state}

      _ ->
        {:error, "Invalid clipboard event: #{inspect(event)}"}
    end
  end

  defp handle_clipboard_operation(emulator_state, :copy, _content) do
    # Copy selected text to clipboard (implementation depends on system clipboard)
    emulator_state
  end

  defp handle_clipboard_operation(emulator_state, :cut, _content) do
    # Cut selected text to clipboard and remove from buffer
    emulator_state
  end

  defp handle_clipboard_operation(emulator_state, _op, _content) do
    # Unknown operation, return unchanged state
    emulator_state
  end
end
