defmodule Raxol.Terminal.StateManager do
  @moduledoc """
  Manages terminal state transitions and state stack operations.
  This module is responsible for maintaining and manipulating the terminal's state.

  This module implements the StateManager behavior for consistent state management
  patterns across the codebase while maintaining its specific terminal functionality.
  """

  # Using consolidated StateManager for functional state operations
  alias Raxol.Core.StateManager

  # Delegate common state operations to consolidated StateManager
  def init(), do: {:ok, %{}}
  def init(opts), do: {:ok, Keyword.get(opts, :initial_state, %{})}
  defdelegate get(state, key), to: StateManager
  defdelegate get(state, key, default), to: StateManager
  defdelegate put(state, key, value), to: StateManager
  defdelegate update(state, key, func), to: StateManager
  defdelegate delete(state, key), to: StateManager
  defdelegate clear(state), to: StateManager
  defdelegate validate(state), to: StateManager
  defdelegate merge(state1, state2), to: StateManager

  alias Raxol.Terminal.Emulator
  require Raxol.Core.Runtime.Log

  @doc """
  Saves the current terminal state.
  Returns {:ok, updated_emulator}.
  """
  @spec save_state(Emulator.t()) :: {:ok, Emulator.t()}
  def save_state(emulator) do
    current_state = %{
      cursor: get_cursor_state(emulator),
      screen: get_screen_state(emulator),
      modes: get_mode_state(emulator)
    }

    new_stack = [current_state | emulator.state_stack]
    {:ok, %{emulator | state_stack: new_stack}}
  end

  @doc """
  Restores the most recently saved terminal state.
  Returns {:ok, updated_emulator}.
  """
  @spec restore_state(Emulator.t()) :: {:ok, Emulator.t()}
  def restore_state(emulator) do
    case emulator.state_stack do
      [state | new_stack] ->
        emulator = apply_saved_state(emulator, state)
        {:ok, %{emulator | state_stack: new_stack}}

      [] ->
        {:error, "No saved state to restore"}
    end
  end

  @doc """
  Clears all saved states.
  Returns {:ok, updated_emulator}.
  """
  @spec clear_states(Emulator.t()) :: {:ok, Emulator.t()}
  def clear_states(emulator) do
    {:ok, %{emulator | state_stack: []}}
  end

  @doc """
  Gets the current terminal state.
  Returns the current state map.
  """
  @spec get_current_state(Emulator.t()) :: map()
  def get_current_state(emulator) do
    %{
      cursor: get_cursor_state(emulator),
      screen: get_screen_state(emulator),
      modes: get_mode_state(emulator)
    }
  end

  @doc """
  Updates the current terminal state.
  Returns {:ok, updated_emulator}.
  """
  @spec update_current_state(Emulator.t(), map()) :: {:ok, Emulator.t()}
  def update_current_state(emulator, new_state) do
    emulator = apply_saved_state(emulator, new_state)
    {:ok, emulator}
  end

  # Private helper functions

  defp get_cursor_state(emulator) do
    %{
      position: emulator.cursor.position,
      visible: emulator.cursor.visible,
      style: emulator.cursor.style,
      blink_state: emulator.cursor.blink_state
    }
  end

  defp get_screen_state(emulator) do
    %{
      buffer: emulator.active_buffer,
      scroll_region: emulator.scroll_region,
      charset_state: emulator.charset_state
    }
  end

  defp get_mode_state(emulator) do
    %{
      insert_mode: emulator.mode_manager.insert_mode,
      origin_mode: emulator.mode_manager.origin_mode,
      auto_wrap: emulator.mode_manager.auto_wrap,
      cursor_visible: emulator.mode_manager.cursor_visible
    }
  end

  defp apply_saved_state(emulator, state) do
    emulator
    |> apply_cursor_state(state.cursor)
    |> apply_screen_state(state.screen)
    |> apply_mode_state(state.modes)
  end

  defp apply_cursor_state(emulator, cursor_state) do
    %{emulator | cursor: Map.merge(emulator.cursor, cursor_state)}
  end

  defp apply_screen_state(emulator, screen_state) do
    emulator
    |> Map.put(:active_buffer, screen_state.buffer)
    |> Map.put(:scroll_region, screen_state.scroll_region)
    |> Map.put(:charset_state, screen_state.charset_state)
  end

  defp apply_mode_state(emulator, mode_state) do
    %{emulator | mode_manager: Map.merge(emulator.mode_manager, mode_state)}
  end
end
