defmodule Raxol.Core.KeyboardShortcutsTest do
  @moduledoc """
  Tests for the keyboard shortcuts system, including initialization,
  registration, unregistration, context management, and event handling.
  """
  # Can be async now that we use ProcessStore
  use ExUnit.Case, async: false

  alias Raxol.Core.Events.EventManager
  alias Raxol.Core.KeyboardShortcuts

  setup do
    # Ensure UserPreferences is started
    unless Process.whereis(Raxol.Core.UserPreferences) do
      start_supervised!({Raxol.Core.UserPreferences, [test_mode?: true]})
    end

    # Start and initialize event manager for tests
    case EventManager.start_link(name: EventManager) do
      {:ok, _pid} -> :ok
      {:error, {:already_started, _pid}} -> :ok
      error -> raise "Failed to start EventManager: #{inspect(error)}"
    end

    EventManager.init()

    # Initialize KeyboardShortcuts (this also ensures it's started and registers handlers)
    KeyboardShortcuts.init()

    on_exit(fn ->
      # Clean up - be defensive about process cleanup
      try do
        if Process.whereis(KeyboardShortcuts.Server) do
          KeyboardShortcuts.cleanup()
        end
      catch
        :exit, _ -> :ok
      end

      try do
        if Process.whereis(EventManager) do
          EventManager.cleanup()
        end
      catch
        :exit, _ -> :ok
      end
    end)

    :ok
  end

  describe "init/0" do
    test "initializes keyboard shortcuts registry" do
      # Re-init for test coverage
      KeyboardShortcuts.init()

      # Check server is running
      assert Process.whereis(Raxol.Core.KeyboardShortcuts.ShortcutsServer) != nil
    end
  end

  describe "cleanup/0" do
    test "cleans up keyboard shortcuts registry" do
      # Ensure server is running
      KeyboardShortcuts.ensure_started()
      assert Process.whereis(Raxol.Core.KeyboardShortcuts.ShortcutsServer) != nil

      # Clean up
      KeyboardShortcuts.cleanup()

      # Check server is stopped
      assert Process.whereis(Raxol.Core.KeyboardShortcuts.ShortcutsServer) == nil
    end
  end

  describe "register_shortcut/4" do
    test "registers global shortcut" do
      # Store test process pid
      test_pid = self()

      # Register a global shortcut
      callback = fn -> send(test_pid, :shortcut_triggered) end

      assert :ok =
               KeyboardShortcuts.register_shortcut("Ctrl+S", :save, callback,
                 description: "Save document"
               )

      # Check registry via API
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:global)
      assert Map.has_key?(shortcuts, "ctrl_s")

      # Check shortcut definition
      shortcut = shortcuts["ctrl_s"]
      assert shortcut.name == :save
      assert shortcut.description == "Save document"
      assert shortcut.priority == :medium
      assert shortcut.callback == callback

      # Check key combo parsing
      key_combo = shortcut.key_combo
      assert key_combo.key == :s
      assert :ctrl in key_combo.modifiers
      refute :alt in key_combo.modifiers
      refute :shift in key_combo.modifiers
    end

    test "registers context-specific shortcut" do
      # Register a context-specific shortcut
      assert :ok =
               KeyboardShortcuts.register_shortcut(
                 "Alt+F",
                 :file_menu,
                 fn -> nil end,
                 context: :main_menu,
                 description: "Open file menu",
                 priority: :high
               )

      # Check registry via API
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:main_menu)
      assert Map.has_key?(shortcuts, "alt_f")

      # Check shortcut definition
      shortcut = shortcuts["alt_f"]
      assert shortcut.name == :file_menu
      assert shortcut.description == "Open file menu"
      assert shortcut.priority == :high

      # Check key combo parsing
      key_combo = shortcut.key_combo
      assert key_combo.key == :f
      refute :ctrl in key_combo.modifiers
      assert :alt in key_combo.modifiers
      refute :shift in key_combo.modifiers
    end

    test "handles complex key combinations" do
      # Register with complex key combo
      assert :ok =
               KeyboardShortcuts.register_shortcut(
                 "Ctrl+Shift+Alt+X",
                 :complex,
                 fn -> nil end
               )

      # Check registry via API
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:global)
      # Modifiers are sorted alphabetically by the server
      assert Map.has_key?(shortcuts, "alt_ctrl_shift_x")

      # Check key combo parsing
      key_combo = shortcuts["alt_ctrl_shift_x"].key_combo
      assert key_combo.key == :x
      assert :ctrl in key_combo.modifiers
      assert :alt in key_combo.modifiers
      assert :shift in key_combo.modifiers
    end
  end

  describe "unregister_shortcut/2" do
    test "unregisters global shortcut" do
      # Register a shortcut
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn -> nil end)

      # Verify it exists
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:global)
      assert Map.has_key?(shortcuts, "ctrl_s")

      # Unregister it
      assert :ok = KeyboardShortcuts.unregister_shortcut(:save)

      # Check it's gone
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:global)
      refute Map.has_key?(shortcuts, "ctrl_s")
    end

    test "unregisters context-specific shortcut" do
      # Register a context-specific shortcut
      KeyboardShortcuts.register_shortcut("Alt+F", :file_menu, fn -> nil end, context: :main_menu)

      # Verify it exists
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:main_menu)
      assert Map.has_key?(shortcuts, "alt_f")

      # Unregister it
      assert :ok = KeyboardShortcuts.unregister_shortcut(:file_menu, :main_menu)

      # Check it's gone
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:main_menu)
      refute Map.has_key?(shortcuts, "alt_f")
    end
  end

  describe "set_active_context/1 and get_active_context/0" do
    test "sets and gets current context" do
      # Default should be :global
      assert KeyboardShortcuts.get_active_context() == :global

      # Set context
      assert :ok = KeyboardShortcuts.set_active_context(:editor)

      # Get context
      assert KeyboardShortcuts.get_active_context() == :editor
    end
  end

  describe "get_shortcuts_for_context/1" do
    test "gets global shortcuts" do
      # Register some global shortcuts
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn -> nil end,
        description: "Save document"
      )

      KeyboardShortcuts.register_shortcut("Ctrl+O", :open, fn -> nil end,
        description: "Open document"
      )

      # Get shortcuts
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:global)

      # Check results - shortcuts is a map
      assert map_size(shortcuts) == 2

      # Find save shortcut
      save_shortcut = shortcuts["ctrl_s"]
      assert save_shortcut != nil
      assert save_shortcut.name == :save
      assert save_shortcut.raw == "Ctrl+S"
      assert save_shortcut.description == "Save document"

      # Find open shortcut
      open_shortcut = shortcuts["ctrl_o"]
      assert open_shortcut != nil
      assert open_shortcut.name == :open
      assert open_shortcut.raw == "Ctrl+O"
      assert open_shortcut.description == "Open document"
    end

    test "gets context-specific shortcuts" do
      # Register global shortcut
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn -> nil end,
        description: "Save document"
      )

      # Register context-specific shortcuts
      KeyboardShortcuts.register_shortcut("Alt+F", :file_menu, fn -> nil end,
        context: :main_menu,
        description: "Open file menu"
      )

      KeyboardShortcuts.register_shortcut("Alt+E", :edit_menu, fn -> nil end,
        context: :main_menu,
        description: "Open edit menu"
      )

      # Get shortcuts for context - only returns context shortcuts, not merged with global
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context(:main_menu)

      # Check results - context-specific shortcuts only
      assert map_size(shortcuts) == 2

      # Check for context-specific shortcuts
      file_menu_shortcut = shortcuts["alt_f"]
      assert file_menu_shortcut != nil
      assert file_menu_shortcut.name == :file_menu
      assert file_menu_shortcut.raw == "Alt+F"

      edit_menu_shortcut = shortcuts["alt_e"]
      assert edit_menu_shortcut != nil
      assert edit_menu_shortcut.name == :edit_menu
      assert edit_menu_shortcut.raw == "Alt+E"
    end

    test "gets shortcuts for current context when nil is passed" do
      # Register global shortcut
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn -> nil end)

      # Register context-specific shortcut
      KeyboardShortcuts.register_shortcut("Alt+F", :file_menu, fn -> nil end, context: :main_menu)

      # Set current context
      KeyboardShortcuts.set_active_context(:main_menu)

      # Get shortcuts for current context (nil) - returns context shortcuts only
      shortcuts = KeyboardShortcuts.get_shortcuts_for_context()

      # Should only include context-specific shortcuts (not merged with global)
      assert map_size(shortcuts) == 1
      assert Map.has_key?(shortcuts, "alt_f")
    end
  end

  describe "show_shortcuts_help/0" do
    test "generates help message for shortcuts" do
      # Register some shortcuts
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn -> nil end,
        description: "Save document"
      )

      KeyboardShortcuts.register_shortcut("Ctrl+O", :open, fn -> nil end,
        description: "Open document"
      )

      # Show help
      {:ok, help_message} = KeyboardShortcuts.show_shortcuts_help()

      # Check help message content
      assert String.contains?(
               help_message,
               "Available keyboard shortcuts for Global"
             )

      assert String.contains?(help_message, "Ctrl+S: Save document")
      assert String.contains?(help_message, "Ctrl+O: Open document")
    end
  end

  # NOTE: trigger_shortcut/2 was removed from the module
  # These tests are commented out as they test a non-existent function
  # describe "trigger_shortcut/2" do
  #   test "triggers shortcut callback" do
  #     # Store test process pid
  #     test_pid = self()
  #
  #     # Register a global shortcut
  #     KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn ->
  #       send(test_pid, :save_triggered)
  #     end)
  #
  #     # Trigger shortcut
  #     assert :ok = KeyboardShortcuts.trigger_shortcut(:save)
  #
  #     # Check callback was called
  #     assert_received :save_triggered
  #   end
  #
  #   test "returns error for non-existent shortcut" do
  #     # Trigger non-existent shortcut
  #     assert {:error, :shortcut_not_found} =
  #              KeyboardShortcuts.trigger_shortcut(:nonexistent)
  #   end
  #
  #   test "finds shortcut in specified context" do
  #     # Store test process pid
  #     test_pid = self()
  #
  #     # Register a context-specific shortcut
  #     KeyboardShortcuts.register_shortcut(
  #       "Alt+F",
  #       :file_menu,
  #       fn ->
  #         send(test_pid, :file_menu_triggered)
  #       end,
  #       context: :main_menu
  #     )
  #
  #     # Trigger shortcut in context
  #     assert :ok = KeyboardShortcuts.trigger_shortcut(:file_menu, :main_menu)
  #
  #     # Check callback was called
  #     assert_received :file_menu_triggered
  #   end
  #
  #   test "finds shortcut in current context when nil is passed" do
  #     # Store test process pid
  #     test_pid = self()
  #
  #     # Register a context-specific shortcut
  #     KeyboardShortcuts.register_shortcut(
  #       "Alt+F",
  #       :file_menu,
  #       fn ->
  #         send(test_pid, :file_menu_triggered)
  #       end,
  #       context: :main_menu
  #     )
  #
  #     # Set current context
  #     KeyboardShortcuts.set_active_context(:main_menu)
  #
  #     # Trigger shortcut in current context (nil)
  #     assert :ok = KeyboardShortcuts.trigger_shortcut(:file_menu)
  #
  #     # Check callback was called
  #     assert_received :file_menu_triggered
  #   end
  # end

  describe "handle_keyboard_event/1" do
    test "executes callback for matching shortcut" do
      # Store test process pid
      test_pid = self()

      # Register a global shortcut
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn ->
        send(test_pid, :save_triggered)
      end)

      # Dispatch keyboard event
      EventManager.dispatch({:keyboard_event, {:key, "s", [:ctrl]}})

      # Wait a bit for async processing
      Process.sleep(50)

      # Check callback was called
      assert_received :save_triggered
    end

    test "handles context-specific shortcuts" do
      # Store test process pid
      test_pid = self()

      # Register a context-specific shortcut
      KeyboardShortcuts.register_shortcut(
        "Alt+F",
        :file_menu,
        fn ->
          send(test_pid, :file_menu_triggered)
        end,
        context: :main_menu
      )

      # Set current context
      KeyboardShortcuts.set_active_context(:main_menu)

      # Dispatch keyboard event
      EventManager.dispatch({:keyboard_event, {:key, "f", [:alt]}})

      # Wait a bit for async processing
      Process.sleep(50)

      # Check callback was called
      assert_received :file_menu_triggered
    end

    test "handles multiple modifiers" do
      # Store test process pid
      test_pid = self()

      # Register a shortcut with multiple modifiers
      KeyboardShortcuts.register_shortcut("Ctrl+Shift+X", :cut, fn ->
        send(test_pid, :cut_triggered)
      end)

      # Dispatch keyboard event with multiple modifiers
      EventManager.dispatch({:keyboard_event, {:key, "x", [:ctrl, :shift]}})

      # Wait a bit for async processing
      Process.sleep(50)

      # Check callback was called
      assert_received :cut_triggered
    end

    test "context shortcuts override global shortcuts with same name" do
      # Store test process pid
      test_pid = self()

      # Register global shortcut
      KeyboardShortcuts.register_shortcut("Ctrl+S", :save, fn ->
        send(test_pid, :global_save_triggered)
      end)

      # Register context shortcut with same name
      KeyboardShortcuts.register_shortcut(
        "Alt+S",
        :save,
        fn ->
          send(test_pid, :context_save_triggered)
        end,
        context: :editor
      )

      # Set context
      KeyboardShortcuts.set_active_context(:editor)

      # Dispatch keyboard event matching context shortcut
      EventManager.dispatch({:keyboard_event, {:key, "s", [:alt]}})

      # Wait a bit for async processing
      Process.sleep(50)

      # Should trigger context version
      assert_received :context_save_triggered
      refute_received :global_save_triggered
    end

    test "respects priority order for shortcuts" do
      # Store test process pid
      test_pid = self()

      # Register low priority shortcut
      KeyboardShortcuts.register_shortcut(
        "Ctrl+P",
        :print_low,
        fn ->
          send(test_pid, :print_low_triggered)
        end,
        priority: :low
      )

      # Register high priority shortcut with same key combo
      KeyboardShortcuts.register_shortcut(
        "Ctrl+P",
        :print_high,
        fn ->
          send(test_pid, :print_high_triggered)
        end,
        priority: :high,
        context: :editor
      )

      # Set context
      KeyboardShortcuts.set_active_context(:editor)

      # Dispatch keyboard event
      EventManager.dispatch({:keyboard_event, {:key, "p", [:ctrl]}})

      # Wait a bit for async processing
      Process.sleep(50)

      # Should trigger high priority first
      assert_received :print_high_triggered
    end
  end
end
