// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Drawing;
using System.Reflection;
using Windows.Win32.UI.Accessibility;
using static System.Windows.Forms.ListViewItem;

namespace System.Windows.Forms.Tests.AccessibleObjects;

public class ListViewItem_ListViewItemAccessibleObjectTests
{
    [WinFormsFact]
    public void ListViewItemAccessibleObject_Ctor_ThrowsArgumentNullException()
    {
        using ListView list = new();
        ListViewItem listItem = new();
        list.Items.Add(listItem);

        Type type = listItem.AccessibilityObject.GetType();
        ConstructorInfo ctor = type.GetConstructor([typeof(ListViewItem)]);

        Assert.NotNull(ctor);
        Assert.Throws<TargetInvocationException>(() => ctor.Invoke([null]));

        // item without parent ListView
        ListViewItem itemWithoutList = new();

        Assert.Throws<TargetInvocationException>(() => ctor.Invoke([itemWithoutList]));
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_Ctor_Default()
    {
        using ListView list = new();
        ListViewItem listItem = new();
        list.Items.Add(listItem);

        AccessibleObject accessibleObject = listItem.AccessibilityObject;

        Assert.False(list.IsHandleCreated);
        Assert.NotNull(accessibleObject);
        Assert.Equal(AccessibleRole.ListItem, accessibleObject.Role);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_InGroup_Ctor_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            foreach (bool showGroups in new[] { true, false })
            {
                foreach (bool createHandle in new[] { true, false })
                {
                    yield return new object[] { view, showGroups, createHandle };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_InGroup_Ctor_TestData))]
    public void ListViewItemAccessibleObject_InGroup_Ctor(View view, bool showGroups, bool createHandle)
    {
        using ListView listView = new ListView
        {
            View = view,
            ShowGroups = showGroups
        };

        if (createHandle)
        {
            Assert.NotEqual(IntPtr.Zero, listView.Handle);
        }

        ListViewGroup listviewGroup = new();
        ListViewItem listViewItem1 = new();
        ListViewItem listViewItem2 = new(listviewGroup);
        listviewGroup.Items.Add(listViewItem1);
        listView.Groups.Add(listviewGroup);
        listView.Items.Add(listViewItem2);

        AccessibleObject accessibleObject1 = listViewItem1.AccessibilityObject;
        AccessibleObject accessibleObject2 = listViewItem2.AccessibilityObject;

        Assert.NotNull(accessibleObject1);
        Assert.NotNull(accessibleObject2);
        Assert.Equal(createHandle, listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_GetPropertyValue_ReturnsExpected()
    {
        using ListView list = new();
        ListViewItem listItem = new("ListItem");
        list.Items.Add(listItem);
        AccessibleObject listItemAccessibleObject = listItem.AccessibilityObject;

        string accessibleName = ((BSTR)listItemAccessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_NamePropertyId)).ToStringAndFree();

        Assert.Equal("ListItem", accessibleName);

        string automationId = ((BSTR)listItemAccessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_AutomationIdPropertyId)).ToStringAndFree();

        Assert.Equal("ListViewItem-0", automationId);

        var controlType = (UIA_CONTROLTYPE_ID)(int)listItemAccessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_ControlTypePropertyId);
        UIA_CONTROLTYPE_ID expected = UIA_CONTROLTYPE_ID.UIA_ListItemControlTypeId;

        Assert.Equal(expected, controlType);
        Assert.True((bool)listItemAccessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_IsSelectionItemPatternAvailablePropertyId));
        Assert.True((bool)listItemAccessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_IsScrollItemPatternAvailablePropertyId));
        Assert.True((bool)listItemAccessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_IsInvokePatternAvailablePropertyId));
        Assert.False(list.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_FragmentNavigate_Parent_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            foreach (bool createControl in new[] { true, false })
            {
                foreach (bool virtualMode in new[] { true, false })
                {
                    // View.Tile is not supported by ListView in virtual mode
                    if (view == View.Tile && virtualMode)
                    {
                        continue;
                    }

                    foreach (bool showGroups in new[] { true, false })
                    {
                        yield return new object[] { view, createControl, virtualMode, showGroups };
                    }
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Parent_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_Parent_ReturnExpected(View view, bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, createControl, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Equal(GetExpectedParent(0), accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(GetExpectedParent(1), accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(GetExpectedParent(2), accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(GetExpectedParent(3), accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));

        AccessibleObject GetExpectedParent(int index)
        {
            return Application.UseVisualStyles
                && showGroups
                && !virtualMode
                && listView.View != View.List
                ? listView.Items[index].Group is not null
                    ? listView.Groups[0].AccessibilityObject
                    : listView.DefaultGroup.AccessibilityObject
                : listView.AccessibilityObject;
        }

        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_FragmentNavigate_Sibling_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            foreach (bool virtualMode in new[] { true, false })
            {
                // View.Tile is not supported by ListView in virtual mode
                if (view == View.Tile && virtualMode)
                {
                    continue;
                }

                foreach (bool showGroups in new[] { true, false })
                {
                    yield return new object[] { view, virtualMode, showGroups };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Sibling_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_NextSibling_ReturnExpected_IfHandleIsCreated(View view, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, true, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        if (Application.UseVisualStyles && showGroups && !virtualMode && listView.View != View.List)
        {
            Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
            Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
            Assert.Equal(accessibleObject4, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
            Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        }
        else
        {
            Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
            Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
            Assert.Equal(accessibleObject4, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
            Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        }

        Assert.True(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Sibling_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_NextSibling_ReturnExpected_IfHandleIsNotCreated(View view, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, false, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Sibling_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_PreviousSibling_ReturnExpected_IfHandleIsCreated(View view, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, true, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        if (Application.UseVisualStyles && showGroups && !virtualMode && listView.View != View.List)
        {
            Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
            Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
            Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
            Assert.Equal(accessibleObject3, accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        }
        else
        {
            Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
            Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
            Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
            Assert.Equal(accessibleObject3, accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        }

        Assert.True(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Sibling_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_PreviousSibling_ReturnExpected_IfHandleIsNotCreated(View view, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, false, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.False(listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_FragmentNavigate_Child_TestData()
    {
        foreach (View view in new View[] { View.List, View.LargeIcon, View.SmallIcon })
        {
            foreach (bool createControl in new[] { true, false })
            {
                foreach (bool virtualMode in new[] { true, false })
                {
                    foreach (bool showGroups in new[] { true, false })
                    {
                        yield return new object[] { view, createControl, virtualMode, showGroups };
                    }
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Child_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_FirstChild_ReturnExpected(View view, bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, createControl, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, true)]
    [InlineData(true, false)]
    [InlineData(false, true)]
    [InlineData(false, false)]
    public void ListViewItemAccessibleObject_FragmentNavigate_FirstChild_ReturnExpected_Tile_View(bool createControl, bool showGroups)
    {
        using ListView listView = GetListViewWithData(View.Tile, createControl, virtualMode: false, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, true)]
    [InlineData(true, false)]
    [InlineData(false, true)]
    [InlineData(false, false)]
    public void ListViewItemAccessibleObject_FragmentNavigate_FirstChild_ReturnExpected_Details_View_IfHandleCreated(bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(View.Details, createControl: true, virtualMode: virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Equal(listView.Items[0].SubItems[0].AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(listView.Items[1].SubItems[0].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(listView.Items[2].SubItems[0].AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(listView.Items[3].SubItems[0].AccessibilityObject, accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.True(listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_FragmentNavigate_Details_TestData()
    {
        foreach (bool createControl in new[] { true, false })
        {
            foreach (bool virtualMode in new[] { true, false })
            {
                foreach (bool showGroups in new[] { true, false })
                {
                    yield return new object[] { createControl, virtualMode, showGroups };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Details_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_FirstChildReturnExpected_Details_View_IfHandleNotCreated(bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(View.Details, createControl, virtualMode, showGroups, columnCount: 3, subItemCount: 3);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;

        Assert.Equal(listView.Items[0].SubItems[0].AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(listView.Items[1].SubItems[0].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_FirstChild));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Child_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_ReturnExpected(View view, bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithData(view, createControl, virtualMode, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, true)]
    [InlineData(true, false)]
    [InlineData(false, true)]
    [InlineData(false, false)]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_ReturnExpected_Tile_View(bool createControl, bool showGroups)
    {
        using ListView listView = GetListViewWithData(View.Tile, createControl, virtualMode: false, showGroups);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;
        AccessibleObject accessibleObject4 = listView.Items[3].AccessibilityObject;

        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Null(accessibleObject4.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Details_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_ReturnExpected_Details_View(bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(View.Details, createControl, virtualMode, showGroups, columnCount: 3, subItemCount: 3);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;

        Assert.Equal(listView.Items[0].SubItems[2].AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Equal(listView.Items[1].SubItems[2].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Details_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_DoesNotReturnNull_WithoutSubItems_Detail_Views(bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(View.Details, createControl, virtualMode, showGroups, columnCount: 3, subItemCount: 0);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        IRawElementProviderFragment.Interface lastChild1 = accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);
        IRawElementProviderFragment.Interface lastChild2 = accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);

        Assert.NotNull(lastChild1);
        Assert.NotNull(lastChild2);
        Assert.NotEqual(listView.Items[0].SubItems[0].AccessibilityObject, lastChild1);
        Assert.NotEqual(listView.Items[1].SubItems[0].AccessibilityObject, lastChild2);
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Details_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_ReturnsExpected_SingleColumn_Detail_Views(bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(View.Details, createControl, virtualMode, showGroups, columnCount: 1, subItemCount: 5);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        IRawElementProviderFragment.Interface lastChild1 = accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);
        IRawElementProviderFragment.Interface lastChild2 = accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);

        Assert.Equal(listView.Items[0].SubItems[0].AccessibilityObject, lastChild1);
        Assert.Equal(listView.Items[1].SubItems[0].AccessibilityObject, lastChild2);
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Details_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_ReturnsExpected_AfterAddingSubItem_Detail_Views(bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(View.Details, createControl, virtualMode, showGroups, columnCount: 2, subItemCount: 0);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        IRawElementProviderFragment.Interface lastChild1 = accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);
        IRawElementProviderFragment.Interface lastChild2 = accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);

        Assert.NotNull(lastChild1);
        Assert.NotNull(lastChild2);
        Assert.Equal(1, listView.Items[0].SubItems.Count);
        Assert.Equal(1, listView.Items[1].SubItems.Count);

        listView.Items[0].SubItems.Add(new ListViewSubItem() { Text = $"SubItem 0" });
        listView.Items[1].SubItems.Add(new ListViewSubItem() { Text = $"SubItem 0" });

        lastChild1 = accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);
        lastChild2 = accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);

        Assert.Equal(listView.Items[0].SubItems[1].AccessibilityObject, lastChild1);
        Assert.Equal(listView.Items[1].SubItems[1].AccessibilityObject, lastChild2);
        Assert.Equal(2, listView.Items[0].SubItems.Count);
        Assert.Equal(2, listView.Items[1].SubItems.Count);
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Details_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_LastChild_ReturnsExpected_AfterRemovingSubItem_Detail_Views(bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(View.Details, createControl, virtualMode, showGroups, columnCount: 2, subItemCount: 1);

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        IRawElementProviderFragment.Interface lastChild1 = accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);
        IRawElementProviderFragment.Interface lastChild2 = accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);

        Assert.Equal(listView.Items[0].SubItems[1].AccessibilityObject, lastChild1);
        Assert.Equal(listView.Items[1].SubItems[1].AccessibilityObject, lastChild2);
        Assert.Equal(2, listView.Items[0].SubItems.Count);
        Assert.Equal(2, listView.Items[1].SubItems.Count);

        listView.Items[0].SubItems.RemoveAt(1);
        listView.Items[1].SubItems.RemoveAt(1);

        lastChild1 = accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);
        lastChild2 = accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_LastChild);

        Assert.NotNull(lastChild1);
        Assert.NotNull(lastChild2);
        Assert.Equal(1, listView.Items[0].SubItems.Count);
        Assert.Equal(1, listView.Items[1].SubItems.Count);
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_View_ShowGroups_VirtualMode_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            // View.Tile is not supported by ListView in virtual mode
            if (view == View.Tile)
            {
                continue;
            }

            foreach (bool showGroups in new[] { true, false })
            {
                yield return new object[] { view, showGroups };
                yield return new object[] { view, showGroups };
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_View_ShowGroups_VirtualMode_TestData))]
    public void ListViewItemAccessibleObject_FragmentNavigate_ListWithItems_VirtualMode_VirtualListSize1_ReturnsExpected(View view, bool showGroups)
    {
        using ListView listView = new ListView
        {
            View = view,
            VirtualMode = true,
            ShowGroups = showGroups
        };

        listView.VirtualListSize = 1;

        ListViewItem listItem1 = new(new string[]
        {
            "Test A",
            "Alpha"
        }, -1);

        ListViewItem listItem2 = new(new string[]
        {
            "Test B",
            "Beta"
        }, -1);

        ListViewItem listItem3 = new(new string[]
        {
            "Test C",
            "Gamma"
        }, -1);

        listView.RetrieveVirtualItem += (s, e) =>
        {
            e.Item = e.ItemIndex switch
            {
                0 => listItem1,
                1 => listItem2,
                2 => listItem3,
                _ => throw new NotImplementedException()
            };
        };

        listItem1.SetItemIndex(listView, 0);
        listItem2.SetItemIndex(listView, 1);
        listItem3.SetItemIndex(listView, 2);

        listView.CreateControl();

        AccessibleObject accessibleObject1 = listItem1.AccessibilityObject;
        AccessibleObject accessibleObject2 = listItem2.AccessibilityObject;
        AccessibleObject accessibleObject3 = listItem3.AccessibilityObject;

        // First list view item
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));

        // Second list view item
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));

        // Third list view item
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));

        // Parent
        Assert.Equal(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent), listView.AccessibilityObject);
        Assert.Equal(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent), listView.AccessibilityObject);
        Assert.Equal(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent), listView.AccessibilityObject);
        Assert.True(listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_State_TestData()
    {
        AccessibleStates defaultStates = AccessibleStates.Selectable | AccessibleStates.Focusable | AccessibleStates.MultiSelectable;
        AccessibleStates selectedStates = defaultStates | AccessibleStates.Selected | AccessibleStates.Focused;

        foreach (View view in Enum.GetValues(typeof(View)))
        {
            foreach (bool selected in new[] { true, false })
            {
                foreach (bool createHandle in new[] { true, false })
                {
                    AccessibleStates expectedState = selected ? selectedStates : defaultStates;
                    yield return new object[] { view, selected, expectedState, createHandle };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_State_TestData))]
    public void ListViewItemAccessibleObject_State_ReturnExpected(View view, bool selected, AccessibleStates expectedAccessibleStates, bool createHandle)
    {
        using ListView listView = new ListView
        {
            View = view
        };

        if (createHandle)
        {
            Assert.NotEqual(IntPtr.Zero, listView.Handle);
        }

        ListViewItem listItem1 = new(new string[] { "Test A", "Alpha" }, -1);
        listView.Items.Add(listItem1);
        listView.Items[0].Selected = selected;
        AccessibleObject accessibleObject = listView.Items[0].AccessibilityObject;

        Assert.Equal(expectedAccessibleStates, accessibleObject.State);
        Assert.Equal(createHandle, listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_State_VirtualMode_TestData()
    {
        AccessibleStates defaultStates = AccessibleStates.Selectable | AccessibleStates.Focusable | AccessibleStates.MultiSelectable;
        AccessibleStates selectedStates = defaultStates | AccessibleStates.Selected | AccessibleStates.Focused;

        foreach (View view in Enum.GetValues(typeof(View)))
        {
            // View.Tile is not supported by ListView in virtual mode
            if (view == View.Tile)
            {
                continue;
            }

            foreach (bool selected in new[] { true, false })
            {
                foreach (bool createHandle in new[] { true, false })
                {
                    AccessibleStates expectedState = selected ? selectedStates : defaultStates;
                    yield return new object[] { view, selected, expectedState, createHandle };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_State_VirtualMode_TestData))]
    public void ListViewItemAccessibleObject_State_Virtual_ModeReturnExpected(View view, bool selected, AccessibleStates expectedAccessibleStates, bool createHandle)
    {
        using ListView listView = new ListView
        {
            View = view,
            VirtualMode = true,
            VirtualListSize = 1
        };

        ListViewItem listItem1 = new(new string[] { "Test A", "Alpha" }, -1);

        listView.RetrieveVirtualItem += (s, e) =>
        {
            e.Item = e.ItemIndex switch
            {
                0 => listItem1,
                _ => throw new NotImplementedException()
            };
        };

        listItem1.SetItemIndex(listView, 0);

        if (createHandle)
        {
            Assert.NotEqual(IntPtr.Zero, listView.Handle);
        }

        if (selected)
        {
            listView.Items[0].Selected = true;
        }

        AccessibleObject accessibleObject = listView.Items[0].AccessibilityObject;

        Assert.Equal(expectedAccessibleStates, accessibleObject.State);
        Assert.Equal(createHandle, listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_Bounds_TestData()
    {
        foreach (bool virtualMode in new[] { true, false })
        {
            foreach (View view in Enum.GetValues(typeof(View)))
            {
                // View.Tile is not supported by ListView in virtual mode
                if (virtualMode && view == View.Tile)
                {
                    continue;
                }

                foreach (bool showGroups in new[] { true, false })
                {
                    yield return new object[] { view, showGroups, virtualMode };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_Bounds_TestData))]
    public void ListViewItemAccessibleObject_Bounds_ReturnExpected_IfHandleIsCreated(View view, bool showGroups, bool virtualMode)
    {
        using ListView listView = GetBoundsListView(view, showGroups, virtualMode);
        listView.CreateControl();

        Assert.NotEqual(Rectangle.Empty, listView.Items[0].AccessibilityObject.Bounds);
        if (listView.GroupsDisplayed)
        {
            Assert.Equal(Rectangle.Empty, listView.Items[1].AccessibilityObject.Bounds);
        }
        else
        {
            Assert.NotEqual(Rectangle.Empty, listView.Items[1].AccessibilityObject.Bounds);
        }

        Assert.NotEqual(Rectangle.Empty, listView.Items[2].AccessibilityObject.Bounds);

        Assert.True(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_Bounds_TestData))]
    public void ListViewItemAccessibleObject_Bounds_ReturnExpected_IfHandleIsNotCreated(View view, bool showGroups, bool virtualMode)
    {
        using ListView listView = GetBoundsListView(view, showGroups, virtualMode);

        Assert.Equal(Rectangle.Empty, listView.Items[0].AccessibilityObject.Bounds);
        Assert.Equal(Rectangle.Empty, listView.Items[1].AccessibilityObject.Bounds);
        Assert.Equal(Rectangle.Empty, listView.Items[2].AccessibilityObject.Bounds);

        Assert.False(listView.IsHandleCreated);
    }

    private void AddItemToListView(ListView listView, ListViewItem listViewItem, bool virtualMode)
    {
        if (virtualMode)
        {
            listView.RetrieveVirtualItem += (s, e) =>
            {
                e.Item = e.ItemIndex switch
                {
                    0 => listViewItem,
                    _ => throw new NotImplementedException()
                };
            };

            listViewItem.SetItemIndex(listView, 0);
        }
        else
        {
            listView.Items.Add(listViewItem);
        }
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_IsTogglePatternSupported_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            // View.Tile does not support enabled CheckBoxes
            if (view == View.Tile)
            {
                continue;
            }

            foreach (bool showGroups in new[] { true, false })
            {
                foreach (bool createHandle in new[] { true, false })
                {
                    foreach (bool virtualMode in new[] { true, false })
                    {
                        foreach (bool checkboxes in new[] { true, false })
                        {
                            yield return new object[] { view, showGroups, createHandle, virtualMode, checkboxes };
                        }
                    }
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_IsTogglePatternSupported_TestData))]
    public void ListViewItemAccessibleObject_IsTogglePatternSupported_ReturnExpected(View view, bool showGroups, bool createHandle, bool virtualMode, bool checkboxesEnabled)
    {
        using ListView listView = new()
        {
            View = view,
            VirtualMode = virtualMode,
            VirtualListSize = 1,
            CheckBoxes = checkboxesEnabled,
            ShowGroups = showGroups
        };

        ListViewItem listViewItem = new("Item");
        AddItemToListView(listView, listViewItem, virtualMode);

        if (createHandle)
        {
            Assert.NotEqual(IntPtr.Zero, listView.Handle);
        }

        Assert.Equal(checkboxesEnabled, listViewItem.AccessibilityObject.IsPatternSupported(UIA_PATTERN_ID.UIA_TogglePatternId));
        Assert.Equal(createHandle, listView.IsHandleCreated);
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_ToggleState_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            // View.Tile does not support enabled CheckBoxes
            if (view == View.Tile)
            {
                continue;
            }

            foreach (bool showGroups in new[] { true, false })
            {
                foreach (bool createHandle in new[] { true, false })
                {
                    foreach (bool virtualMode in new[] { true, false })
                    {
                        yield return new object[] { view, showGroups, createHandle, virtualMode };
                    }
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_ToggleState_TestData))]
    public void ListViewItemAccessibleObject_ToggleState_ReturnExpected(View view, bool showGroups, bool createHandle, bool virtualMode)
    {
        using ListView listView = new()
        {
            View = view,
            VirtualMode = virtualMode,
            VirtualListSize = 1,
            CheckBoxes = true,
            ShowGroups = showGroups
        };

        ListViewItem listViewItem = new("Item");
        AddItemToListView(listView, listViewItem, virtualMode);

        if (createHandle)
        {
            Assert.NotEqual(IntPtr.Zero, listView.Handle);
        }

        AccessibleObject listViewItemAccessibleObject = listViewItem.AccessibilityObject;

        Assert.Equal(ToggleState.ToggleState_Off, listViewItemAccessibleObject.ToggleState);

        listViewItem.Checked = true;

        Assert.Equal(ToggleState.ToggleState_On, listViewItemAccessibleObject.ToggleState);

        listViewItem.Checked = false;

        Assert.Equal(ToggleState.ToggleState_Off, listViewItemAccessibleObject.ToggleState);
        Assert.Equal(createHandle, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_ToggleState_TestData))]
    public void ListViewItemAccessibleObject_Toggle_Invoke(View view, bool showGroups, bool createHandle, bool virtualMode)
    {
        using ListView listView = new()
        {
            View = view,
            VirtualMode = virtualMode,
            VirtualListSize = 1,
            CheckBoxes = true,
            ShowGroups = showGroups
        };

        ListViewItem listViewItem = new("Item");
        AddItemToListView(listView, listViewItem, virtualMode);

        if (createHandle)
        {
            Assert.NotEqual(IntPtr.Zero, listView.Handle);
        }

        AccessibleObject listViewItemAccessibleObject = listViewItem.AccessibilityObject;

        Assert.Equal(ToggleState.ToggleState_Off, listViewItemAccessibleObject.ToggleState);
        Assert.False(listViewItem.Checked);

        listViewItemAccessibleObject.Toggle();

        Assert.Equal(ToggleState.ToggleState_On, listViewItemAccessibleObject.ToggleState);
        Assert.True(listViewItem.Checked);

        // toggle again
        listViewItemAccessibleObject.Toggle();

        Assert.Equal(ToggleState.ToggleState_Off, listViewItemAccessibleObject.ToggleState);
        Assert.False(listViewItem.Checked);
        Assert.Equal(createHandle, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_ReturnsExpected_InvisibleItems(View view)
    {
        using ListView listView = GetListViewItemWithInvisibleItems(view);

        Assert.Null(GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.True(listView.IsHandleCreated);

        AccessibleObject GetAccessibleObject(int index) => listView.Groups[0].Items[index].AccessibilityObject;
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_ReturnsExpected_InvisibleItems_AfterAddingItems(View view)
    {
        if (!Application.UseVisualStyles)
        {
            return;
        }

        using ListView listView = GetListViewItemWithInvisibleItems(view);

        Assert.Null(GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));

        listView.Items.Add(listView.Groups[0].Items[0]);
        listView.Items.Add(listView.Groups[0].Items[3]);

        Assert.Null(GetAccessibleObject(0).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(0).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(GetAccessibleObject(0), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Equal(GetAccessibleObject(3), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(3).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(GetAccessibleObject(3).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.True(listView.IsHandleCreated);

        AccessibleObject GetAccessibleObject(int index) => listView.Groups[0].Items[index].AccessibilityObject;
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view)
    {
        using ListView listView = GetListViewItemWithInvisibleItems(view);

        Assert.Null(GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));

        listView.Items.RemoveAt(1);

        Assert.Null(GetAccessibleObject(0).FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
        Assert.Null(GetAccessibleObject(0).FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.True(listView.IsHandleCreated);

        AccessibleObject GetAccessibleObject(int index) => listView.Groups[0].Items[index].AccessibilityObject;
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Parent_TestData))]
    public void ListViewItemAccessibleObject_GetChildCount_ReturnsExpected(View view, bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(view, createControl, virtualMode, showGroups, columnCount: 3, subItemCount: 1);

        // We return 3 for "Details" view because the number of children is equal to the number of columns
        // We return 0 for the "Tile" view because it is limited in the unit tests(all cases are covered in the MauiListViewTests)
        // We return -1 for the "List", "LargeIcon", "SmallIcon" view because they don't support subitems.
        int expectedCount = listView.IsHandleCreated && listView.SupportsListViewSubItems
            ? view == View.Details ? 3 : 0
            : -1;

        Assert.Equal(expectedCount, listView.Items[0].AccessibilityObject.GetChildCount());
        Assert.Equal(expectedCount, listView.Items[1].AccessibilityObject.GetChildCount());
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Parent_TestData))]
    public void ListViewItemAccessibleObject_GetChild_ReturnsExpected(View view, bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(view, createControl, virtualMode, showGroups, columnCount: 3, subItemCount: 1);
        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;

        Assert.Null(accessibleObject1.GetChild(-1));
        Assert.Null(accessibleObject2.GetChild(-1));
        if (view == View.Details)
        {
            Assert.Equal(listView.Items[0].SubItems[0].AccessibilityObject, accessibleObject1.GetChild(0));
            Assert.Equal(listView.Items[0].SubItems[1].AccessibilityObject, accessibleObject1.GetChild(1));
            Assert.Equal(
                ((ListViewItem.ListViewItemDetailsAccessibleObject)accessibleObject1).GetDetailsSubItemOrFake(2),
                accessibleObject1.GetChild(2));

            Assert.Equal(listView.Items[1].SubItems[0].AccessibilityObject, accessibleObject2.GetChild(0));
            Assert.Equal(listView.Items[1].SubItems[1].AccessibilityObject, accessibleObject2.GetChild(1));
            Assert.Equal(
                ((ListViewItem.ListViewItemDetailsAccessibleObject)accessibleObject2).GetDetailsSubItemOrFake(2),
                accessibleObject2.GetChild(2));
        }
        else
        {
            Assert.Null(accessibleObject1.GetChild(0));
            Assert.Null(accessibleObject1.GetChild(1));
            Assert.Null(accessibleObject1.GetChild(2));

            Assert.Null(accessibleObject2.GetChild(0));
            Assert.Null(accessibleObject2.GetChild(1));
            Assert.Null(accessibleObject2.GetChild(2));
        }

        Assert.Null(accessibleObject1.GetChild(3));
        Assert.Null(accessibleObject2.GetChild(3));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_FragmentNavigate_Parent_TestData))]
    public void ListViewItemAccessibleObject_GetChild_ReturnsExpected_ForManySubItems(View view, bool createControl, bool virtualMode, bool showGroups)
    {
        using ListView listView = GetListViewWithSubItemData(view, createControl, virtualMode, showGroups, columnCount: 3, subItemCount: 10);
        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;

        Assert.Null(accessibleObject1.GetChild(-1));
        Assert.Null(accessibleObject2.GetChild(-1));
        if (view == View.Details)
        {
            Assert.Equal(listView.Items[0].SubItems[0].AccessibilityObject, accessibleObject1.GetChild(0));
            Assert.Equal(listView.Items[0].SubItems[1].AccessibilityObject, accessibleObject1.GetChild(1));
            Assert.Equal(listView.Items[0].SubItems[2].AccessibilityObject, accessibleObject1.GetChild(2));

            Assert.Equal(listView.Items[1].SubItems[0].AccessibilityObject, accessibleObject2.GetChild(0));
            Assert.Equal(listView.Items[1].SubItems[1].AccessibilityObject, accessibleObject2.GetChild(1));
            Assert.Equal(listView.Items[1].SubItems[2].AccessibilityObject, accessibleObject2.GetChild(2));
        }
        else
        {
            Assert.Null(accessibleObject1.GetChild(0));
            Assert.Null(accessibleObject1.GetChild(1));
            Assert.Null(accessibleObject1.GetChild(2));

            Assert.Null(accessibleObject2.GetChild(0));
            Assert.Null(accessibleObject2.GetChild(1));
            Assert.Null(accessibleObject2.GetChild(2));
        }

        Assert.Null(accessibleObject1.GetChild(3));
        Assert.Null(accessibleObject2.GetChild(3));
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    private ListView GetListViewWithSubItemData(
        View view,
        bool createControl,
        bool virtualMode,
        bool showGroups,
        int columnCount,
        int subItemCount)
    {
        ListView listView = new()
        {
            View = view,
            ShowGroups = showGroups,
            VirtualMode = virtualMode,
            VirtualListSize = 2
        };

        ListViewItem listItem1 = new("Item 1");
        ListViewItem listItem2 = new("Item 2");

        if (!virtualMode)
        {
            ListViewGroup listViewGroup = new("Test");
            listItem2.Group = listViewGroup;
            listView.Groups.Add(listViewGroup);
        }

        for (int i = 0; i < subItemCount; i++)
        {
            listItem1.SubItems.Add(new ListViewSubItem() { Text = $"SubItem {i}" });
            listItem2.SubItems.Add(new ListViewSubItem() { Text = $"SubItem {i}" });
        }

        for (int i = 0; i < columnCount; i++)
        {
            listView.Columns.Add(new ColumnHeader($"Column {i}"));
        }

        if (virtualMode)
        {
            listView.RetrieveVirtualItem += (s, e) =>
            {
                e.Item = e.ItemIndex switch
                {
                    0 => listItem1,
                    1 => listItem2,
                    _ => throw new NotImplementedException()
                };
            };

            listItem1.SetItemIndex(listView, 0);
            listItem2.SetItemIndex(listView, 0);
        }
        else
        {
            listView.Items.AddRange((ListViewItem[])[listItem1, listItem2]);
        }

        if (createControl)
        {
            listView.CreateControl();
        }

        return listView;
    }

    private ListView GetListViewWithData(View view, bool createControl, bool virtualMode, bool showGroups, bool groupsEnabled = true, bool itemsInGroup = true, ListViewGroupCollapsedState state = ListViewGroupCollapsedState.Default)
    {
        ListView listView = new()
        {
            View = view,
            ShowGroups = showGroups,
            VirtualMode = virtualMode,
            VirtualListSize = 4
        };

        ListViewItem listItem1 = new(new string[] { "Test Item 1", "Item A" }, -1);
        ListViewItem listItem2 = new("Group item 2");
        ListViewItem listItem3 = new("Item 3");
        ListViewItem listItem4 = new(new string[] { "Test Item 4", "Item B", "Item C", "Item D" }, -1);

        if (!virtualMode && groupsEnabled)
        {
            ListViewGroup listViewGroup = new("Test") { CollapsedState = state };
            listView.Groups.Add(listViewGroup);
            if (itemsInGroup)
            {
                listItem1.Group = listViewGroup;
                listItem2.Group = listViewGroup;
            }
        }

        listView.Columns.Add(new ColumnHeader() { Name = "Column 1" });
        listView.Columns.Add(new ColumnHeader() { Name = "Column 2" });
        listView.Columns.Add(new ColumnHeader() { Name = "Column 3" });

        if (virtualMode)
        {
            listView.RetrieveVirtualItem += (s, e) =>
            {
                e.Item = e.ItemIndex switch
                {
                    0 => listItem1,
                    1 => listItem2,
                    2 => listItem3,
                    3 => listItem4,
                    _ => throw new NotImplementedException()
                };
            };

            listItem1.SetItemIndex(listView, 0);
            listItem2.SetItemIndex(listView, 1);
            listItem3.SetItemIndex(listView, 2);
            listItem4.SetItemIndex(listView, 3);
        }
        else
        {
            listView.Items.Add(listItem1);
            listView.Items.Add(listItem2);
            listView.Items.Add(listItem3);
            listView.Items.Add(listItem4);
        }

        if (createControl)
        {
            listView.CreateControl();
        }

        return listView;
    }

    private ListView GetListViewItemWithInvisibleItems(View view)
    {
        ListView listView = new() { View = view };
        listView.CreateControl();
        ListViewGroup listViewGroup = new("Test group");
        ListViewItem listViewInvisibleItem1 = new("Invisible item 1");
        ListViewItem listViewVisibleItem1 = new("Visible item 1");
        ListViewItem listViewInvisibleItem2 = new("Invisible item 1");
        ListViewItem listViewVisibleItem2 = new("Visible item 1");

        listView.Groups.Add(listViewGroup);
        listView.Items.AddRange((ListViewItem[])[listViewVisibleItem1, listViewVisibleItem2]);
        listViewGroup.Items.AddRange((ListViewItem[])
        [
            listViewInvisibleItem1, listViewVisibleItem1,
            listViewVisibleItem2, listViewInvisibleItem2
        ]);

        return listView;
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_Parent_ReturnsExpected_AfterAddingGroup(View view)
    {
        if (!Application.UseVisualStyles)
        {
            return;
        }

        using ListView listView = new()
        {
            View = view,
            ShowGroups = true
        };

        listView.Items.Add(new ListViewItem("Item 1"));
        listView.Items.Add(new ListViewItem("Item 2"));
        listView.Items.Add(new ListViewItem("Item 3"));
        listView.Columns.Add(new ColumnHeader());

        listView.CreateControl();

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;

        Assert.Equal(listView.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        listView.Groups.Add(new ListViewGroup());
        listView.Items[1].Group = listView.Groups[0];

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.Groups[0].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_Parent_ReturnsExpected_AfterRemovingGroup(View view)
    {
        if (!Application.UseVisualStyles)
        {
            return;
        }

        using ListView listView = new()
        {
            View = view,
            ShowGroups = true
        };

        listView.Groups.Add(new ListViewGroup());

        listView.Items.Add(new ListViewItem("Item 1"));
        listView.Items.Add(new ListViewItem("Item 2", group: listView.Groups[0]));
        listView.Items.Add(new ListViewItem("Item 3"));
        listView.Columns.Add(new ColumnHeader());

        listView.CreateControl();

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.Groups[0].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        listView.Groups.RemoveAt(0);

        Assert.Equal(listView.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_Parent_ReturnsExpected_AfterUpdatingGroup(View view)
    {
        if (!Application.UseVisualStyles)
        {
            return;
        }

        using ListView listView = new()
        {
            View = view,
            ShowGroups = true
        };

        listView.Groups.Add(new ListViewGroup());

        listView.Items.Add(new ListViewItem("Item 1"));
        listView.Items.Add(new ListViewItem("Item 2", group: listView.Groups[0]));
        listView.Items.Add(new ListViewItem("Item 3"));
        listView.Columns.Add(new ColumnHeader());

        listView.CreateControl();

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.Groups[0].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        listView.Groups[0].Items.Insert(0, listView.Items[0]);

        Assert.Equal(listView.Groups[0].AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.Groups[0].AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_FragmentNavigate_Sibling_Parent_ReturnsExpected_ListView()
    {
        using ListView listView = new()
        {
            View = View.List,
            ShowGroups = true
        };

        listView.Items.Add(new ListViewItem("Item 1"));
        listView.Items.Add(new ListViewItem("Item 2"));
        listView.Items.Add(new ListViewItem("Item 3"));
        listView.Columns.Add(new ColumnHeader());

        listView.CreateControl();

        AccessibleObject accessibleObject1 = listView.Items[0].AccessibilityObject;
        AccessibleObject accessibleObject2 = listView.Items[1].AccessibilityObject;
        AccessibleObject accessibleObject3 = listView.Items[2].AccessibilityObject;

        Assert.Equal(listView.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        // Since "List" mode does not support ListViewGroups, adding a ListViewGroup should not affect the operation of availability objects
        listView.Groups.Add(new ListViewGroup());
        listView.Items[1].Group = listView.Groups[0];

        Assert.Equal(listView.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        // Since "List" mode does not support ListViewGroups, updating a ListViewGroup should not affect the operation of availability objects
        listView.Groups[0].Items.Insert(0, listView.Items[0]);

        Assert.Equal(listView.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        // Since "List" mode does not support ListViewGroups, removing a ListViewGroup should not affect the operation of availability objects
        listView.Groups.RemoveAt(0);

        Assert.Equal(listView.AccessibilityObject, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject2, accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Null(accessibleObject1.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Equal(accessibleObject3, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject1, accessibleObject2.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));

        Assert.Equal(listView.AccessibilityObject, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_Parent));
        Assert.Null(accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_NextSibling));
        Assert.Equal(accessibleObject2, accessibleObject3.FragmentNavigate(NavigateDirection.NavigateDirection_PreviousSibling));
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.List)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_GetChildIndex_ReturnsExpected(View view)
    {
        using ListView listView = new() { View = view };
        listView.Items.Add(new ListViewItem(["Item 1", "SubItem 1", "SubItem 2"]));
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        AccessibleObject accessibleObject = listView.Items[0].AccessibilityObject;

        if (view == View.Details)
        {
            Assert.Equal(0, accessibleObject.GetChildIndex(listView.Items[0].SubItems[0].AccessibilityObject));
            Assert.Equal(1, accessibleObject.GetChildIndex(listView.Items[0].SubItems[1].AccessibilityObject));
            Assert.Equal(-1, accessibleObject.GetChildIndex(listView.Items[0].SubItems[2].AccessibilityObject));
        }
        else
        {
            Assert.Equal(-1, accessibleObject.GetChildIndex(listView.Items[0].SubItems[0].AccessibilityObject));
            Assert.Equal(-1, accessibleObject.GetChildIndex(listView.Items[0].SubItems[1].AccessibilityObject));
            Assert.Equal(-1, accessibleObject.GetChildIndex(listView.Items[0].SubItems[2].AccessibilityObject));
        }

        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.List)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_GetChildIndex_ReturnsMinusOne_IfChildIsNull(View view)
    {
        using ListView listView = new() { View = view };
        listView.Items.Add(new ListViewItem(["Item 1", "SubItem 1", "SubItem 2"]));

        Assert.Equal(-1, listView.Items[0].AccessibilityObject.GetChildIndex(null));
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.List)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_GetChildIndex_ReturnsMinusOne_IfSubItemNotExists(View view)
    {
        using ListView listView = new() { View = view };
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Items.Add(new ListViewItem(["Item 1", "SubItem 11", "SubItem 12"]));
        listView.Items.Add(new ListViewItem(["Item 2", "SubItem 21", "SubItem 22"]));

        Assert.Equal(-1, listView.Items[0].AccessibilityObject.GetChildIndex(listView.Items[1].SubItems[1].AccessibilityObject));
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_GetChildIndex_ReturnsExpected_ForFakeSubItem()
    {
        using ListView listView = new() { View = View.Details };
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Items.Add(new ListViewItem(["Item 1"]));
        ListViewItemDetailsAccessibleObject accessibleObject = (ListViewItemDetailsAccessibleObject)listView.Items[0].AccessibilityObject;

        Assert.Equal(0, accessibleObject.GetChildIndex(listView.Items[0].SubItems[0].AccessibilityObject));
        Assert.Equal(1, accessibleObject.GetChildIndex(accessibleObject.GetDetailsSubItemOrFake(1)));
        Assert.Equal(2, accessibleObject.GetChildIndex(accessibleObject.GetDetailsSubItemOrFake(2)));
        Assert.Equal(3, accessibleObject.GetChildIndex(accessibleObject.GetDetailsSubItemOrFake(3)));
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, 1)]
    [InlineData(false, 0)]
    public void ListViewItemAccessibleObject_GetChildIndex_ReturnsExpected_Image(bool hasImage, int expectedFirstSubItemIndex)
    {
        using ImageList imageCollection = new();
        imageCollection.Images.Add(Form.DefaultIcon);
        using ListView listView = new()
        {
            View = View.Details,
            SmallImageList = imageCollection
        };

        listView.Columns.Add(new ColumnHeader());
        ListViewItem listViewItem = new("Item 1", imageIndex: hasImage ? 0 : -1);
        listView.Items.Add(listViewItem);
        var accessibleObject = (ListViewItemDetailsAccessibleObject)listView.Items[0].AccessibilityObject;
        
        Assert.Equal(expectedFirstSubItemIndex, accessibleObject.GetChildIndex(listView.Items[0].SubItems[0].AccessibilityObject));
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.List)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_GetChildIndex_ReturnsMinusOne_ForInvalidChild(View view)
    {
        using ListView listView = new() { View = view };
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Columns.Add(new ColumnHeader());
        listView.Items.Add(new ListViewItem(["Item 1", "SubItem 11", "SubItem 12"]));

        Assert.Equal(-1, listView.Items[0].AccessibilityObject.GetChildIndex(listView.AccessibilityObject));
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(View.Details)]
    [InlineData(View.LargeIcon)]
    [InlineData(View.List)]
    [InlineData(View.SmallIcon)]
    [InlineData(View.Tile)]
    public void ListViewItemAccessibleObject_ReturnExpectedType(View view)
    {
        using ListView listView = new() { View = view };
        listView.Items.Add(new ListViewItem("Test"));

        switch (view)
        {
            case View.Details:
                Assert.IsType<ListViewItemDetailsAccessibleObject>(listView.Items[0].AccessibilityObject);
                break;
            case View.LargeIcon:
                Assert.IsType<ListViewItemLargeIconAccessibleObject>(listView.Items[0].AccessibilityObject);
                break;
            case View.List:
                Assert.IsType<ListViewItemListAccessibleObject>(listView.Items[0].AccessibilityObject);
                break;
            case View.SmallIcon:
                Assert.IsType<ListViewItemSmallIconAccessibleObject>(listView.Items[0].AccessibilityObject);
                break;
            case View.Tile:
                Assert.IsType<ListViewItemTileAccessibleObject>(listView.Items[0].AccessibilityObject);
                break;
        }
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_ReturnExpectedType_TestData()
    {
        foreach (View oldView in Enum.GetValues(typeof(View)))
        {
            foreach (View newView in Enum.GetValues(typeof(View)))
            {
                if (oldView != newView)
                {
                    yield return new object[] { oldView, newView };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_ReturnExpectedType_TestData))]
    public void ListViewItemAccessibleObject_ReturnExpectedType_AfterChangingView(View oldView, View newView)
    {
        using ListView listView = new() { View = oldView };
        listView.Items.Add(new ListViewItem("Test"));
        CheckAccessibleObject();

        listView.View = newView;

        CheckAccessibleObject();

        void CheckAccessibleObject()
        {
            switch (listView.View)
            {
                case View.Details:
                    Assert.IsType<ListViewItemDetailsAccessibleObject>(listView.Items[0].AccessibilityObject);
                    break;
                case View.LargeIcon:
                    Assert.IsType<ListViewItemLargeIconAccessibleObject>(listView.Items[0].AccessibilityObject);
                    break;
                case View.List:
                    Assert.IsType<ListViewItemListAccessibleObject>(listView.Items[0].AccessibilityObject);
                    break;
                case View.SmallIcon:
                    Assert.IsType<ListViewItemSmallIconAccessibleObject>(listView.Items[0].AccessibilityObject);
                    break;
                case View.Tile:
                    Assert.IsType<ListViewItemTileAccessibleObject>(listView.Items[0].AccessibilityObject);
                    break;
            }
        }
    }

    [WinFormsTheory]
    [InlineData(View.Details, View.LargeIcon)]
    [InlineData(View.Details, View.List)]
    [InlineData(View.Details, View.SmallIcon)]
    [InlineData(View.Details, View.Tile)]
    [InlineData(View.LargeIcon, View.Details)]
    [InlineData(View.LargeIcon, View.List)]
    [InlineData(View.LargeIcon, View.SmallIcon)]
    [InlineData(View.LargeIcon, View.Tile)]
    [InlineData(View.List, View.Details)]
    [InlineData(View.List, View.LargeIcon)]
    [InlineData(View.List, View.SmallIcon)]
    [InlineData(View.List, View.Tile)]
    [InlineData(View.SmallIcon, View.Details)]
    [InlineData(View.SmallIcon, View.LargeIcon)]
    [InlineData(View.SmallIcon, View.List)]
    [InlineData(View.SmallIcon, View.Tile)]
    [InlineData(View.Tile, View.Details)]
    [InlineData(View.Tile, View.LargeIcon)]
    [InlineData(View.Tile, View.List)]
    [InlineData(View.Tile, View.SmallIcon)]
    public void ListViewItemAccessibleObject_GetChild_ReturnException_AfterChangingView(View oldView, View newView)
    {
        using ListView listView = new() { View = oldView };
        listView.Items.Add(new ListViewItem(["1", "2"]));
        AccessibleObject accessibleObject = listView.Items[0].AccessibilityObject;
        Assert.Null(accessibleObject.GetChild(0));

        listView.View = newView;

        Assert.Throws<InvalidOperationException>(() => accessibleObject.GetChild(0));
    }

    [WinFormsTheory]
    [InlineData(View.Details, View.LargeIcon)]
    [InlineData(View.Details, View.List)]
    [InlineData(View.Details, View.SmallIcon)]
    [InlineData(View.Details, View.Tile)]
    [InlineData(View.LargeIcon, View.Details)]
    [InlineData(View.LargeIcon, View.List)]
    [InlineData(View.LargeIcon, View.SmallIcon)]
    [InlineData(View.LargeIcon, View.Tile)]
    [InlineData(View.List, View.Details)]
    [InlineData(View.List, View.LargeIcon)]
    [InlineData(View.List, View.SmallIcon)]
    [InlineData(View.List, View.Tile)]
    [InlineData(View.SmallIcon, View.Details)]
    [InlineData(View.SmallIcon, View.LargeIcon)]
    [InlineData(View.SmallIcon, View.List)]
    [InlineData(View.SmallIcon, View.Tile)]
    [InlineData(View.Tile, View.Details)]
    [InlineData(View.Tile, View.LargeIcon)]
    [InlineData(View.Tile, View.List)]
    [InlineData(View.Tile, View.SmallIcon)]
    public void ListViewItemAccessibleObject_GetChildCount_ReturnException_AfterChangingView(View oldView, View newView)
    {
        using ListView listView = new() { View = oldView };
        listView.Items.Add(new ListViewItem(["1"]));
        AccessibleObject accessibleObject = listView.Items[0].AccessibilityObject;
        Assert.NotEqual(2, accessibleObject.GetChildCount());

        listView.View = newView;

        Assert.Throws<InvalidOperationException>(() => accessibleObject.GetChildCount());
    }

    public static IEnumerable<object[]> ListViewItemAccessibleObject_GetPropertyValue_TestData()
    {
        foreach (View view in Enum.GetValues(typeof(View)))
        {
            foreach (bool createControl in new[] { true, false })
            {
                foreach (bool virtualMode in new[] { true, false })
                {
                    // View.Tile is not supported by ListView in virtual mode
                    if (virtualMode && view == View.Tile)
                    {
                        continue;
                    }

                    foreach (bool showGroups in new[] { true, false })
                    {
                        foreach (bool groupsEnabled in new[] { true, false })
                        {
                            foreach (bool itemsInGroup in new[] { true, false })
                            {
                                foreach (ListViewGroupCollapsedState state in Enum.GetValues(typeof(ListViewGroupCollapsedState)))
                                {
                                    yield return new object[] { view, createControl, virtualMode, showGroups, groupsEnabled, itemsInGroup, state };
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(ListViewItemAccessibleObject_GetPropertyValue_TestData))]
    public void ListViewItemAccessibleObject_GetPropertyValue_ReturnsFalseWhenCollapsedOrOffScreen(View view, bool createControl, bool virtualMode, bool showGroups, bool groupsEnabled, bool itemsInGroup, ListViewGroupCollapsedState state)
    {
        using ListView listView = GetListViewWithData(view, createControl, virtualMode, showGroups, groupsEnabled, itemsInGroup, state);
        ListViewItemBaseAccessibleObject accessibleObject = (ListViewItemBaseAccessibleObject)listView.Items[0].AccessibilityObject;

        bool expected = listView.GroupsDisplayed && listView.Items[0].Group?.CollapsedState == ListViewGroupCollapsedState.Collapsed;
        bool actual = (bool)accessibleObject.GetPropertyValue(UIA_PROPERTY_ID.UIA_IsOffscreenPropertyId);

        Assert.Equal(expected, actual);
        Assert.Equal(createControl, listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_IsDisconnected_WhenListViewReleasesUiaProvider()
    {
        using ListView listView = new();
        ListViewItem item = new("ListItem");
        listView.Items.Add(item);
        EnforceAccessibleObjectCreation(item);

        listView.ReleaseUiaProvider(listView.HWND);

        Assert.Null(item.TestAccessor().Dynamic._accessibilityObject);
        Assert.True(listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_IsDisconnected_WhenListViewIsCleared()
    {
        using ListView listView = new();
        ListViewItem item = new("ListItem");
        listView.Items.Add(item);
        EnforceAccessibleObjectCreation(item);

        listView.Clear();

        Assert.Null(item.TestAccessor().Dynamic._accessibilityObject);
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_IsDisconnected_WhenItemsAreCleared()
    {
        using ListView listView = new();
        ListViewItem item = new("ListItem");
        listView.Items.Add(item);
        EnforceAccessibleObjectCreation(item);

        listView.Items.Clear();

        Assert.Null(item.TestAccessor().Dynamic._accessibilityObject);
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_IsDisconnected_WhenItemIsRemoved()
    {
        using ListView listView = new();
        ListViewItem item = new("ListItem");
        listView.Items.Add(item);
        EnforceAccessibleObjectCreation(item);

        listView.Items.Remove(item);

        Assert.Null(item.TestAccessor().Dynamic._accessibilityObject);
        Assert.False(listView.IsHandleCreated);
    }

    [WinFormsFact]
    public void ListViewItemAccessibleObject_IsDisconnected_WhenItemIsReplaced()
    {
        using ListView listView = new();
        ListViewItem item = new("ListItem");
        listView.Items.Add(item);
        EnforceAccessibleObjectCreation(item);

        listView.Items[0] = new ListViewItem();

        Assert.Null(item.TestAccessor().Dynamic._accessibilityObject);
        Assert.False(listView.IsHandleCreated);
    }

    private static void EnforceAccessibleObjectCreation(ListViewItem listViewItem)
    {
        _ = listViewItem.AccessibilityObject;
        Assert.NotNull(listViewItem.TestAccessor().Dynamic._accessibilityObject);
    }

    private ListView GetBoundsListView(View view, bool showGroups, bool virtualMode)
    {
        ListView listView = new()
        {
            View = view,
            ShowGroups = showGroups,
            VirtualMode = virtualMode,
            VirtualListSize = 3
        };

        ListViewItem listViewItem1 = new("Item1");
        ListViewItem listViewItem2 = new("Item2");
        ListViewItem listViewItem3 = new("Item3");

        if (!virtualMode)
        {
            ListViewGroup lvgroup1 = new()
            {
                Header = "CollapsibleGroup1",
                CollapsedState = ListViewGroupCollapsedState.Expanded
            };

            ListViewGroup lvgroup2 = new()
            {
                Header = "CollapsibleGroup2",
                CollapsedState = ListViewGroupCollapsedState.Collapsed
            };

            listView.Groups.Add(lvgroup1);
            listView.Groups.Add(lvgroup2);

            listViewItem1.Group = lvgroup1;
            listViewItem2.Group = lvgroup2;
        }

        if (virtualMode)
        {
            listView.RetrieveVirtualItem += (s, e) =>
            {
                e.Item = e.ItemIndex switch
                {
                    0 => listViewItem1,
                    1 => listViewItem1,
                    2 => listViewItem2,
                    _ => throw new NotImplementedException()
                };
            };

            listViewItem1.SetItemIndex(listView, 0);
            listViewItem2.SetItemIndex(listView, 1);
            listViewItem3.SetItemIndex(listView, 2);
        }
        else
        {
            listView.Items.Add(listViewItem1);
            listView.Items.Add(listViewItem2);
            listView.Items.Add(listViewItem3);
        }

        listView.Columns.Add(new ColumnHeader());
        return listView;
    }
}
