defmodule Raxol.Terminal.Integration.Buffer do
  @moduledoc """
  Handles buffer and cursor management for the terminal.
  """

  alias Raxol.Terminal.{
    Buffer.Scroll,
    Integration.State,
    ScreenBuffer.Manager
  }

  alias Raxol.Terminal.Cursor.Manager, as: CursorManager

  @doc """
  Writes text to the terminal buffer.
  """
  def write(%State{} = state, text) do
    # Update the buffer manager with the new text
    {:ok, buffer_manager} = Manager.write(state.buffer_manager, text)

    # Update the cursor position - handle different cursor_manager types
    cursor_manager =
      case state.cursor_manager do
        nil ->
          nil

        cursor when is_map(cursor) ->
          # If it's a Cursor.Manager struct, use its update function
          {x, y} = CursorManager.get_position(cursor)
          CursorManager.set_position(cursor, {x + String.length(text), y})

        cursor ->
          # For other types, return as-is
          cursor
      end

    # Update the state
    State.update(state,
      buffer_manager: buffer_manager,
      cursor_manager: cursor_manager
    )
  end

  @doc """
  Clears the terminal buffer.
  """
  def clear(%State{} = state) do
    # Clear the buffer manager
    buffer_manager = Manager.clear(state.buffer_manager)

    # Reset the cursor position - handle different cursor_manager types
    cursor_manager =
      case state.cursor_manager do
        nil ->
          nil

        cursor when is_map(cursor) ->
          CursorManager.set_position(cursor, {0, 0})

        cursor ->
          cursor
      end

    # Update the state
    State.update(state,
      buffer_manager: buffer_manager,
      cursor_manager: cursor_manager
    )
  end

  @doc """
  Scrolls the terminal buffer.
  """
  def scroll(%State{} = state, direction, amount \\ 1) do
    # Update the scroll buffer
    scroll_buffer = Scroll.scroll(state.scroll_buffer, direction, amount)

    # Update the buffer manager's visible region (expects non_neg_integer, not region)
    # Scroll.get_visible_region always returns {offset, height}
    {scroll_offset, _height} = Scroll.get_visible_region(scroll_buffer)

    buffer_manager =
      Manager.update_visible_region(
        state.buffer_manager,
        scroll_offset
      )

    # Update the state
    State.update(state,
      buffer_manager: buffer_manager,
      scroll_buffer: scroll_buffer
    )
  end

  @doc """
  Moves the cursor to a specific position.
  """
  def move_cursor(%State{} = state, x, y) do
    # Update the cursor position - handle different cursor_manager types
    cursor_manager =
      case state.cursor_manager do
        nil ->
          nil

        cursor when is_map(cursor) ->
          CursorManager.set_position(cursor, {x, y})

        cursor ->
          cursor
      end

    # Update the state
    State.update(state, cursor_manager: cursor_manager)
  end

  @doc """
  Gets the current cursor position.
  """
  def get_cursor_position(%State{} = state) do
    case state.cursor_manager do
      nil ->
        {0, 0}

      cursor when is_map(cursor) ->
        CursorManager.get_position(cursor)

      _cursor ->
        {0, 0}
    end
  end

  @doc """
  Gets the current visible content.
  """
  def get_visible_content(%State{} = state) do
    Manager.get_visible_content(state.buffer_manager)
  end

  @doc """
  Gets the current scroll position.
  """
  def get_scroll_position(%State{} = state) do
    Scroll.get_position(state.scroll_buffer)
  end

  @doc """
  Gets the total number of lines in the buffer.
  """
  def get_total_lines(%State{} = state) do
    Manager.get_total_lines(state.buffer_manager)
  end

  @doc """
  Gets the number of visible lines.
  """
  def get_visible_lines(%State{} = state) do
    Manager.get_visible_lines(state.buffer_manager)
  end

  @doc """
  Resizes the terminal buffer.
  """
  def resize(%State{} = state, width, height) do
    # Resize the buffer manager
    buffer_manager = Manager.resize(state.buffer_manager, width, height)

    # Update the scroll buffer
    scroll_buffer = Scroll.resize(state.scroll_buffer, height)

    # Constrain cursor position to new dimensions
    cursor_manager =
      case state.cursor_manager do
        nil ->
          nil

        cursor when is_map(cursor) ->
          {x, y} = CursorManager.get_position(cursor)
          # Constrain to new bounds
          new_x = max(0, min(x, width - 1))
          new_y = max(0, min(y, height - 1))
          CursorManager.set_position(cursor, {new_x, new_y})

        cursor ->
          cursor
      end

    # Update the state
    State.update(state,
      buffer_manager: buffer_manager,
      scroll_buffer: scroll_buffer,
      cursor_manager: cursor_manager
    )
  end
end
