# Unit tests for preferences_grid_base.py.
#
# Copyright 2026 Igalia, S.L.
# Author: Joanmarie Diggs <jdiggs@igalia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA  02110-1301 USA.

# pylint: disable=wrong-import-position
# pylint: disable=protected-access
# pylint: disable=import-outside-toplevel

"""Unit tests for preferences_grid_base.py."""

from __future__ import annotations

from typing import Any

import gi
import pytest

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

from orca.preferences_grid_base import (
    AutoPreferencesGrid,
    BooleanPreferenceControl,
    CategoryListBoxRow,
    CommandListBoxRow,
    ControlType,
    EnumPreferenceControl,
    FloatRangePreferenceControl,
    FocusManagedListBox,
    IntRangePreferenceControl,
    PreferencesGridBase,
    RadioButtonWithActions,
    SelectionPreferenceControl,
    StackedPreferencesHelper,
)


@pytest.mark.unit
class TestPreferenceControlDataclasses:
    """Test preference control dataclass creation and properties."""

    def test_boolean_preference_control_creation(self) -> None:
        """Test BooleanPreferenceControl can be created with required fields."""

        getter_called: list[bool] = [False]
        setter_value: list[bool | None] = [None]

        def getter() -> bool:
            getter_called[0] = True
            return True

        def setter(value: bool) -> bool:
            setter_value[0] = value
            return True

        control = BooleanPreferenceControl(
            label="Test Label",
            getter=getter,
            setter=setter,
            prefs_key="testKey",
        )

        assert control.label == "Test Label"
        assert control.prefs_key == "testKey"
        assert control.getter() is True
        assert getter_called[0] is True
        control.setter(False)
        assert setter_value[0] is False

    def test_boolean_preference_control_optional_fields(self) -> None:
        """Test BooleanPreferenceControl optional fields have correct defaults."""

        control = BooleanPreferenceControl(label="Test", getter=lambda: True, setter=lambda x: True)

        assert control.prefs_key is None
        assert control.member_of is None
        assert control.determine_sensitivity is None
        assert control.apply_immediately is True

    def test_int_range_preference_control_creation(self) -> None:
        """Test IntRangePreferenceControl can be created with all fields."""

        control = IntRangePreferenceControl(
            label="Volume",
            minimum=0,
            maximum=100,
            getter=lambda: 50,
            setter=lambda x: True,
            prefs_key="volume",
        )

        assert control.label == "Volume"
        assert control.minimum == 0
        assert control.maximum == 100
        assert control.getter() == 50

    def test_float_range_preference_control_creation(self) -> None:
        """Test FloatRangePreferenceControl can be created with all fields."""

        control = FloatRangePreferenceControl(
            label="Rate",
            minimum=0.0,
            maximum=1.0,
            getter=lambda: 0.5,
            setter=lambda x: True,
        )

        assert control.label == "Rate"
        assert control.minimum == 0.0
        assert control.maximum == 1.0
        assert control.getter() == 0.5

    def test_enum_preference_control_creation(self) -> None:
        """Test EnumPreferenceControl can be created with options and values."""

        control = EnumPreferenceControl(
            label="Style",
            options=["Option A", "Option B", "Option C"],
            values=[1, 2, 3],
            getter=lambda: 2,
            setter=lambda x: True,
        )

        assert control.label == "Style"
        assert len(control.options) == 3
        assert control.values == [1, 2, 3]
        assert control.getter() == 2

    def test_selection_preference_control_creation(self) -> None:
        """Test SelectionPreferenceControl can be created with actions callback."""

        def get_actions(_value: Any) -> list[tuple[str, str, Any]]:
            return [("Edit", "edit-symbolic", lambda: None)]

        control = SelectionPreferenceControl(
            label="Profile",
            options=["Default", "Custom"],
            getter=lambda: "Default",
            setter=lambda x: True,
            get_actions_for_option=get_actions,
        )

        assert control.label == "Profile"
        assert len(control.options) == 2
        assert control.get_actions_for_option is not None
        actions = control.get_actions_for_option("Default")
        assert len(actions) == 1
        assert actions[0][0] == "Edit"


@pytest.mark.unit
class TestHelperClasses:
    """Test helper classes in preferences_grid_base."""

    def test_category_list_box_row_stores_category(self) -> None:
        """Test CategoryListBoxRow stores and returns category identifier."""

        row = CategoryListBoxRow("speech_settings")
        assert row.category == "speech_settings"

    def test_category_list_box_row_is_gtk_list_box_row(self) -> None:
        """Test CategoryListBoxRow inherits from Gtk.ListBoxRow."""

        row = CategoryListBoxRow("test")
        assert isinstance(row, Gtk.ListBoxRow)

    def test_command_list_box_row_properties(self) -> None:
        """Test CommandListBoxRow property getters and setters."""

        row = CommandListBoxRow()
        assert row.command is None
        assert row.vbox is None
        assert row.binding_label is None

        mock_command = object()
        mock_vbox = Gtk.Box()
        mock_label = Gtk.Label()

        row.command = mock_command
        row.vbox = mock_vbox
        row.binding_label = mock_label

        assert row.command is mock_command
        assert row.vbox is mock_vbox
        assert row.binding_label is mock_label

    def test_radio_button_with_actions_stores_buttons(self) -> None:
        """Test RadioButtonWithActions stores action buttons list."""

        radio = RadioButtonWithActions(label="Test Option")
        assert not radio.action_buttons

        button1 = Gtk.Button()
        button2 = Gtk.Button()
        radio.action_buttons = [button1, button2]

        assert len(radio.action_buttons) == 2
        assert radio.action_buttons[0] is button1

    def test_radio_button_with_actions_is_gtk_radio_button(self) -> None:
        """Test RadioButtonWithActions inherits from Gtk.RadioButton."""

        radio = RadioButtonWithActions(label="Test")
        assert isinstance(radio, Gtk.RadioButton)

    def test_stacked_preferences_helper_initial_state(self) -> None:
        """Test StackedPreferencesHelper initializes with None values."""

        helper = StackedPreferencesHelper()
        assert helper.stack is None
        assert helper.categories_listbox is None
        assert helper.detail_listbox is None
        assert not helper.disable_widgets
        assert helper.on_category_activated_callback is None

    def test_stacked_preferences_helper_register_widgets(self) -> None:
        """Test StackedPreferencesHelper can register widgets to disable."""

        helper = StackedPreferencesHelper()
        button1 = Gtk.Button()
        button2 = Gtk.Button()

        helper.register_disable_widgets(button1, button2)
        assert len(helper.disable_widgets) == 2
        assert button1 in helper.disable_widgets

    def test_stacked_preferences_helper_show_categories(self) -> None:
        """Test StackedPreferencesHelper show_categories enables registered widgets."""

        helper = StackedPreferencesHelper()
        helper.stack = Gtk.Stack()
        cat_label = Gtk.Label(label="Categories")
        det_label = Gtk.Label(label="Detail")
        helper.stack.add_named(cat_label, "categories")
        helper.stack.add_named(det_label, "detail")

        # Show the stack to ensure children are realized
        helper.stack.show_all()  # pylint: disable=no-member
        helper.stack.set_visible_child_name("detail")

        button = Gtk.Button()
        button.set_sensitive(False)
        helper.register_disable_widgets(button)

        helper.show_categories()
        assert button.get_sensitive() is True
        # Verify the visible child is the categories child
        assert helper.stack.get_visible_child() is cat_label

    def test_stacked_preferences_helper_show_detail(self) -> None:
        """Test StackedPreferencesHelper show_detail disables registered widgets."""

        helper = StackedPreferencesHelper()
        helper.stack = Gtk.Stack()
        cat_label = Gtk.Label(label="Categories")
        det_label = Gtk.Label(label="Detail")
        helper.stack.add_named(cat_label, "categories")
        helper.stack.add_named(det_label, "detail")

        # Show the stack to ensure children are realized
        helper.stack.show_all()  # pylint: disable=no-member
        helper.stack.set_visible_child_name("categories")

        button = Gtk.Button()
        button.set_sensitive(True)
        helper.register_disable_widgets(button)

        helper.show_detail()
        assert button.get_sensitive() is False
        # Verify the visible child is the detail child
        assert helper.stack.get_visible_child() is det_label


@pytest.mark.unit
class TestFocusManagedListBox:
    """Test FocusManagedListBox focus management."""

    def test_focus_managed_listbox_initial_state(self) -> None:
        """Test FocusManagedListBox initializes with correct settings."""

        listbox = FocusManagedListBox()
        assert listbox.get_selection_mode() == Gtk.SelectionMode.NONE
        assert listbox.get_can_focus() is False

    def test_focus_managed_listbox_add_row_with_widget(self) -> None:
        """Test FocusManagedListBox tracks added rows and widgets."""

        listbox = FocusManagedListBox()
        row = Gtk.ListBoxRow()
        switch = Gtk.Switch()

        listbox.add_row_with_widget(row, switch)

        assert len(listbox._widgets) == 1
        assert len(listbox._rows) == 1
        assert listbox._widgets[0] is switch
        assert listbox._rows[0] is row

    def test_focus_managed_listbox_get_last_row(self) -> None:
        """Test FocusManagedListBox.get_last_row returns last added row."""

        listbox = FocusManagedListBox()
        assert listbox.get_last_row() is None

        row1 = Gtk.ListBoxRow()
        row2 = Gtk.ListBoxRow()
        listbox.add_row_with_widget(row1, Gtk.Switch())
        listbox.add_row_with_widget(row2, Gtk.Switch())

        assert listbox.get_last_row() is row2

    def test_focus_managed_listbox_multiple_rows(self) -> None:
        """Test FocusManagedListBox handles multiple rows correctly."""

        listbox = FocusManagedListBox()

        for _i in range(5):
            row = Gtk.ListBoxRow()
            switch = Gtk.Switch()
            listbox.add_row_with_widget(row, switch)

        assert len(listbox._widgets) == 5
        assert len(listbox._rows) == 5


@pytest.mark.unit
class TestPreferencesGridBase:
    """Test PreferencesGridBase UI helper methods."""

    def test_preferences_grid_base_initialization(self) -> None:
        """Test PreferencesGridBase initializes with correct properties."""

        grid = PreferencesGridBase("Test Tab")
        assert grid._tab_label == "Test Tab"
        assert grid._has_unsaved_changes is False
        assert grid.get_border_width() == 24  # pylint: disable=no-member

    def test_preferences_grid_base_get_label(self) -> None:
        """Test PreferencesGridBase.get_label returns Gtk.Label."""

        grid = PreferencesGridBase("My Tab")
        label = grid.get_label()

        assert isinstance(label, Gtk.Label)
        assert label.get_text() == "My Tab"

    def test_preferences_grid_base_has_changes(self) -> None:
        """Test PreferencesGridBase.has_changes tracks unsaved changes."""

        grid = PreferencesGridBase("Test")
        assert grid.has_changes() is False

        grid._has_unsaved_changes = True
        assert grid.has_changes() is True

    def test_create_frame_returns_frame_and_grid(self) -> None:
        """Test _create_frame returns Gtk.Frame and content Gtk.Grid."""

        grid = PreferencesGridBase("Test")
        frame, content = grid._create_frame("Section Label")

        assert isinstance(frame, Gtk.Frame)
        assert isinstance(content, Gtk.Grid)
        assert frame.get_child() is content

    def test_create_label_with_mnemonic(self) -> None:
        """Test _create_label creates label with mnemonic support."""

        grid = PreferencesGridBase("Test")
        label = grid._create_label("_Test Label")

        assert isinstance(label, Gtk.Label)
        # Label should use underline for mnemonic
        assert label.get_use_underline() is True

    def test_create_switch_row(self) -> None:
        """Test _create_switch_row creates row with label and switch."""

        grid = PreferencesGridBase("Test")
        handler_called = [False]

        def handler(_switch, _param):
            handler_called[0] = True

        row, switch, label = grid._create_switch_row("Enable Feature", handler, state=True)

        assert isinstance(row, Gtk.ListBoxRow)
        assert isinstance(switch, Gtk.Switch)
        assert isinstance(label, Gtk.Label)
        assert switch.get_active() is True

    def test_create_slider_row(self) -> None:
        """Test _create_slider_row creates row with label and scale."""

        grid = PreferencesGridBase("Test")
        adjustment = Gtk.Adjustment(
            value=50,
            lower=0,
            upper=100,
            step_increment=1,
            page_increment=10,
        )

        row, scale, label = grid._create_slider_row("Volume", adjustment)

        assert isinstance(row, Gtk.ListBoxRow)
        assert isinstance(scale, Gtk.Scale)
        assert isinstance(label, Gtk.Label)
        assert scale.get_value() == 50

    def test_create_info_listbox(self) -> None:
        """Test _create_info_listbox creates listbox with info message."""

        grid = PreferencesGridBase("Test")
        listbox = grid._create_info_listbox("This is an informational message.")

        assert isinstance(listbox, Gtk.ListBox)
        assert listbox.get_selection_mode() == Gtk.SelectionMode.NONE

    def test_create_scrolled_window(self) -> None:
        """Test _create_scrolled_window wraps widget in scrolled container."""

        grid = PreferencesGridBase("Test")
        label = Gtk.Label(label="Content")
        scrolled = grid._create_scrolled_window(label)

        assert isinstance(scrolled, Gtk.ScrolledWindow)
        assert scrolled.get_hexpand() is True
        assert scrolled.get_vexpand() is True


@pytest.mark.unit
class TestAutoPreferencesGrid:
    """Test AutoPreferencesGrid automatic UI building."""

    def _create_test_grid(self) -> tuple:
        """Create a test grid with various controls for testing."""

        values: dict[str, Any] = {
            "bool_setting": True,
            "int_setting": 5,
            "float_setting": 0.5,
            "enum_setting": "Option B",
        }

        def set_bool(x: bool) -> bool:
            values["bool_setting"] = x
            return True

        def set_int(x: int) -> bool:
            values["int_setting"] = x
            return True

        def set_float(x: float) -> bool:
            values["float_setting"] = x
            return True

        def set_enum(x: str) -> bool:
            values["enum_setting"] = x
            return True

        controls: list[ControlType] = [
            BooleanPreferenceControl(
                label="Enable Feature",
                getter=lambda: values["bool_setting"],
                setter=set_bool,
                prefs_key="boolSetting",
            ),
            IntRangePreferenceControl(
                label="Count",
                minimum=0,
                maximum=10,
                getter=lambda: values["int_setting"],
                setter=set_int,
                prefs_key="intSetting",
            ),
            FloatRangePreferenceControl(
                label="Rate",
                minimum=0.0,
                maximum=1.0,
                getter=lambda: values["float_setting"],
                setter=set_float,
                prefs_key="floatSetting",
            ),
            EnumPreferenceControl(
                label="Style",
                options=["Option A", "Option B", "Option C"],
                getter=lambda: values["enum_setting"],
                setter=set_enum,
                prefs_key="enumSetting",
            ),
        ]

        grid = AutoPreferencesGrid("Test Tab", controls)
        return grid, values, controls

    def test_auto_preferences_grid_builds_widgets(self) -> None:
        """Test AutoPreferencesGrid creates widgets for all controls."""

        grid, _values, controls = self._create_test_grid()

        assert len(grid._widgets) == len(controls)
        assert len(grid._controls) == len(controls)

    def test_auto_preferences_grid_boolean_control_creates_switch(self) -> None:
        """Test AutoPreferencesGrid creates Switch for boolean control."""

        grid, _values, _controls = self._create_test_grid()

        # First control is boolean, should create a Switch
        widget = grid.get_widget(0)
        assert isinstance(widget, Gtk.Switch)
        assert widget.get_active() is True

    def test_auto_preferences_grid_int_range_creates_spinbutton(self) -> None:
        """Test AutoPreferencesGrid creates SpinButton for int range control."""

        grid, _values, _controls = self._create_test_grid()

        widget = grid.get_widget(1)
        assert isinstance(widget, Gtk.SpinButton)
        assert int(widget.get_value()) == 5

    def test_auto_preferences_grid_float_range_creates_scale(self) -> None:
        """Test AutoPreferencesGrid creates Scale for float range control."""

        grid, _values, _controls = self._create_test_grid()

        widget = grid.get_widget(2)
        assert isinstance(widget, Gtk.Scale)
        assert widget.get_value() == pytest.approx(0.5, abs=0.01)

    def test_auto_preferences_grid_enum_creates_combobox(self) -> None:
        """Test AutoPreferencesGrid creates ComboBoxText for enum control."""

        grid, _values, _controls = self._create_test_grid()

        widget = grid.get_widget(3)
        assert isinstance(widget, Gtk.ComboBoxText)
        # Option B is at index 1
        assert widget.get_active() == 1

    def test_auto_preferences_grid_refresh_updates_widgets(self) -> None:
        """Test AutoPreferencesGrid.refresh updates widget values from getters."""

        current_value = [True]

        controls = [
            BooleanPreferenceControl(
                label="Test",
                getter=lambda: current_value[0],
                setter=lambda x: True,
            ),
        ]

        grid = AutoPreferencesGrid("Test", controls)
        switch = grid.get_widget(0)
        assert switch and switch.get_active()

        current_value[0] = False
        grid.refresh()
        assert switch and not switch.get_active()

    def test_auto_preferences_grid_save_settings_calls_setters(self) -> None:
        """Test AutoPreferencesGrid.save_settings calls all setters."""

        grid, values, _controls = self._create_test_grid()

        switch = grid.get_widget(0)
        switch.set_active(False)
        spin = grid.get_widget(1)
        spin.set_value(8)
        result = grid.save_settings()

        assert values["bool_setting"] is False
        assert values["int_setting"] == 8
        assert "boolSetting" in result
        assert result["boolSetting"] is False

    def test_auto_preferences_grid_save_returns_prefs_dict(self) -> None:
        """Test AutoPreferencesGrid.save_settings returns dict with prefs_keys."""

        grid, _values, _controls = self._create_test_grid()

        result = grid.save_settings()

        assert "boolSetting" in result
        assert "intSetting" in result
        assert "floatSetting" in result
        assert "enumSetting" in result

    def test_auto_preferences_grid_reload_clears_changes_flag(self) -> None:
        """Test AutoPreferencesGrid.reload clears unsaved changes flag."""

        grid, _values, _controls = self._create_test_grid()

        grid._has_unsaved_changes = True
        grid.reload()

        assert grid._has_unsaved_changes is False

    def test_auto_preferences_grid_get_widget_returns_none_for_invalid_index(self) -> None:
        """Test AutoPreferencesGrid.get_widget returns None for out-of-range index."""

        grid, _values, controls = self._create_test_grid()

        assert grid.get_widget(-1) is None
        assert grid.get_widget(len(controls)) is None
        assert grid.get_widget(100) is None

    def test_auto_preferences_grid_get_widget_for_control(self) -> None:
        """Test AutoPreferencesGrid.get_widget_for_control finds correct widget."""

        grid, _values, controls = self._create_test_grid()

        widget = grid.get_widget_for_control(controls[0])
        assert widget is not None
        assert isinstance(widget, Gtk.Switch)

        # Non-existent control should return None
        other_control = BooleanPreferenceControl(
            label="Other",
            getter=lambda: True,
            setter=lambda x: True,
        )
        assert grid.get_widget_for_control(other_control) is None

    def test_auto_preferences_grid_with_grouped_controls(self) -> None:
        """Test AutoPreferencesGrid groups controls by member_of field."""

        controls = [
            BooleanPreferenceControl(label="Ungrouped", getter=lambda: True, setter=lambda x: True),
            BooleanPreferenceControl(
                label="Group A Item 1",
                getter=lambda: True,
                setter=lambda x: True,
                member_of="Group A",
            ),
            BooleanPreferenceControl(
                label="Group A Item 2",
                getter=lambda: False,
                setter=lambda x: True,
                member_of="Group A",
            ),
            BooleanPreferenceControl(
                label="Group B Item",
                getter=lambda: True,
                setter=lambda x: True,
                member_of="Group B",
            ),
        ]

        grid = AutoPreferencesGrid("Test", controls)

        assert "Group A" in grid._group_labels
        assert "Group B" in grid._group_labels
        assert len(grid._widgets) == 4

    def test_auto_preferences_grid_sensitivity_callback(self) -> None:
        """Test AutoPreferencesGrid updates sensitivity based on callback."""

        primary_enabled = [True]

        def set_primary(x: bool) -> bool:
            primary_enabled[0] = x
            return True

        controls = [
            BooleanPreferenceControl(
                label="Primary",
                getter=lambda: primary_enabled[0],
                setter=set_primary,
                apply_immediately=True,
            ),
            BooleanPreferenceControl(
                label="Dependent",
                getter=lambda: True,
                setter=lambda x: True,
                determine_sensitivity=lambda: primary_enabled[0],
            ),
        ]

        grid = AutoPreferencesGrid("Test", controls)

        primary_switch = grid.get_widget(0)
        dependent_switch = grid.get_widget(1)

        assert dependent_switch and dependent_switch.get_sensitive()

        grid._initializing = False  # Allow change handlers to run
        assert primary_switch
        primary_switch.set_active(False)

        assert dependent_switch and not dependent_switch.get_sensitive()

    def test_auto_preferences_grid_with_info_message(self) -> None:
        """Test AutoPreferencesGrid displays info message when provided."""

        controls = [
            BooleanPreferenceControl(label="Test", getter=lambda: True, setter=lambda x: True),
        ]

        grid = AutoPreferencesGrid("Test", controls, info_message="This is helpful information.")

        assert grid._info_listbox is not None


@pytest.mark.unit
class TestAutoPreferencesGridSelectionControl:
    """Test AutoPreferencesGrid with SelectionPreferenceControl."""

    def test_selection_control_creates_combobox_without_actions(self) -> None:
        """Test SelectionPreferenceControl without actions creates ComboBoxText."""

        controls = [
            SelectionPreferenceControl(
                label="Choose",
                options=["A", "B", "C"],
                getter=lambda: "B",
                setter=lambda x: True,
            ),
        ]

        grid = AutoPreferencesGrid("Test", controls)
        widget = grid.get_widget(0)

        assert isinstance(widget, Gtk.ComboBoxText)
        assert widget.get_active() == 1

    def test_selection_control_creates_radio_buttons_with_actions(self) -> None:
        """Test SelectionPreferenceControl with actions creates RadioButtons."""

        def get_actions(_value):
            return [("Edit", "edit-symbolic", lambda: None)]

        controls = [
            SelectionPreferenceControl(
                label="Profile",
                options=["Default", "Custom"],
                getter=lambda: "Default",
                setter=lambda x: True,
                get_actions_for_option=get_actions,
            ),
        ]

        grid = AutoPreferencesGrid("Test", controls)
        widget = grid.get_widget(0)

        assert isinstance(widget, RadioButtonWithActions)

    def test_selection_control_with_values_mapping(self) -> None:
        """Test SelectionPreferenceControl maps display options to values."""

        current_value = [100]

        def set_level(x: int) -> bool:
            current_value[0] = x
            return True

        controls = [
            SelectionPreferenceControl(
                label="Level",
                options=["Low", "Medium", "High"],
                values=[50, 100, 150],
                getter=lambda: current_value[0],
                setter=set_level,
                prefs_key="level",
            ),
        ]

        grid = AutoPreferencesGrid("Test", controls)
        widget = grid.get_widget(0)

        assert widget and widget.get_active() == 1

        widget.set_active(2)
        result = grid.save_settings()

        assert current_value[0] == 150
        assert result["level"] == 150


@pytest.mark.unit
class TestPreferencesGridBaseMultiPageStack:
    """Test PreferencesGridBase multi-page stack functionality."""

    def test_create_multi_page_stack_basic(self) -> None:
        """Test _create_multi_page_stack creates stack with categories."""

        class TestGrid(PreferencesGridBase):
            """Test grid."""

            def __init__(self):
                super().__init__("Test")
                self._initializing = True

                child1 = PreferencesGridBase("Child 1")
                child2 = PreferencesGridBase("Child 2")

                enable_listbox, stack, categories = self._create_multi_page_stack(
                    enable_label="Enable",
                    enable_getter=lambda: True,
                    enable_setter=lambda x: True,
                    categories=[
                        ("General", "general", child1),
                        ("Advanced", "advanced", child2),
                    ],
                    main_title="Test Settings",
                )

                self.my_enable_listbox = enable_listbox
                self.my_stack = stack
                self.my_categories = categories
                self._initializing = False

        grid = TestGrid()

        assert grid.my_stack is not None
        assert grid.my_categories is not None
        assert grid.my_enable_listbox is not None

    def test_create_multi_page_stack_without_enable(self) -> None:
        """Test _create_multi_page_stack without enable switch."""

        class TestGrid(PreferencesGridBase):
            """Test grid."""

            def __init__(self):
                super().__init__("Test")
                self._initializing = True

                child = PreferencesGridBase("Child")

                enable_listbox, stack, _categories = self._create_multi_page_stack(
                    enable_label=None,
                    enable_getter=None,
                    enable_setter=None,
                    categories=[("General", "general", child)],
                    main_title="Test",
                )

                self.my_enable_listbox = enable_listbox
                self.my_stack = stack
                self._initializing = False

        grid = TestGrid()

        assert grid.my_enable_listbox is None
        assert grid.my_stack is not None


@pytest.mark.unit
class TestPreferencesGridBaseStackedPreferences:
    """Test PreferencesGridBase stacked drill-down preferences."""

    def test_create_stacked_preferences(self) -> None:
        """Test _create_stacked_preferences creates stack with categories and detail."""

        class TestGrid(PreferencesGridBase):
            """Test grid."""

            def __init__(self):
                super().__init__("Test")
                self.activated_row = None

                stack, categories, detail = self._create_stacked_preferences(
                    on_category_activated=self._on_category,
                    on_detail_row_activated=None,
                )
                self.my_stack = stack
                self.my_categories = categories
                self.my_detail = detail

            def _on_category(self, row):
                self.activated_row = row

        grid = TestGrid()

        assert grid.my_stack is not None
        assert grid.my_categories is not None
        assert grid.my_detail is not None
        assert isinstance(grid.my_stack, Gtk.Stack)
        assert isinstance(grid.my_categories, Gtk.ListBox)
        assert isinstance(grid.my_detail, Gtk.ListBox)

    def test_add_stack_category_row(self) -> None:
        """Test _add_stack_category_row adds category with correct properties."""

        class TestGrid(PreferencesGridBase):
            """Test grid."""

            def __init__(self):
                super().__init__("Test")
                _stack, categories, _detail = self._create_stacked_preferences(
                    on_category_activated=lambda r: None,
                )
                self.my_categories = categories

        grid = TestGrid()

        row = grid._add_stack_category_row(grid.my_categories, "Speech Settings", category="speech")

        assert isinstance(row, CategoryListBoxRow)
        assert row.category == "speech"

    def test_add_stack_category_row_chevron_accessible_role(self) -> None:
        """Test _add_stack_category_row chevron has PUSH_BUTTON accessible role."""

        gi.require_version("Atk", "1.0")
        from gi.repository import Atk

        class TestGrid(PreferencesGridBase):
            """Test grid."""

            def __init__(self):
                super().__init__("Test")
                _stack, categories, _detail = self._create_stacked_preferences(
                    on_category_activated=lambda r: None,
                )
                self.my_categories = categories

        grid = TestGrid()
        row = grid._add_stack_category_row(grid.my_categories, "Test Category", category="test")

        # Find the chevron image in the row's hbox
        hbox = row.get_child()
        chevron = None
        for child in hbox.get_children():
            if isinstance(child, Gtk.Image):
                chevron = child
                break

        assert chevron is not None, "Chevron image not found in category row"

        chevron_accessible = chevron.get_accessible()
        assert chevron_accessible is not None
        assert chevron_accessible.get_role() == Atk.Role.BUTTON
        assert chevron_accessible.get_name() == ""

    def test_show_stack_categories_and_detail(self) -> None:
        """Test _show_stack_categories and _show_stack_detail switch views."""

        class TestGrid(PreferencesGridBase):
            """Test grid."""

            def __init__(self):
                super().__init__("Test")
                stack, categories, detail = self._create_stacked_preferences(
                    on_category_activated=lambda r: None,
                )
                self.my_stack = stack
                self.my_categories = categories
                self.my_detail = detail

        grid = TestGrid()

        grid.my_stack.show_all()  # pylint: disable=no-member

        cat_scrolled = grid.my_stack.get_child_by_name("categories")
        det_scrolled = grid.my_stack.get_child_by_name("detail")

        grid._show_stack_detail()
        assert grid.my_stack.get_visible_child() is det_scrolled

        grid._show_stack_categories()
        assert grid.my_stack.get_visible_child() is cat_scrolled
