using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Client.UI.Services;
using ZB.MOM.WW.OtOpcUa.Client.UI.Tests.Fakes;
using ZB.MOM.WW.OtOpcUa.Client.UI.ViewModels;
using BrowseResult = ZB.MOM.WW.OtOpcUa.Client.Shared.Models.BrowseResult;

namespace ZB.MOM.WW.OtOpcUa.Client.UI.Tests;

/// <summary>Tests for the BrowseTreeViewModel class.</summary>
public class BrowseTreeViewModelTests
{
    private readonly SynchronousUiDispatcher _dispatcher;
    private readonly FakeOpcUaClientService _service;
    private readonly BrowseTreeViewModel _vm;

    /// <summary>Initializes a new instance of the BrowseTreeViewModelTests class.</summary>
    public BrowseTreeViewModelTests()
    {
        _service = new FakeOpcUaClientService
        {
            BrowseResults =
            [
                new BrowseResult("ns=2;s=Node1", "Node1", "Object", true),
                new BrowseResult("ns=2;s=Node2", "Node2", "Variable", false)
            ]
        };
        _dispatcher = new SynchronousUiDispatcher();
        _vm = new BrowseTreeViewModel(_service, _dispatcher);
    }

    /// <summary>Verifies that LoadRootsAsync populates root nodes.</summary>
    [Fact]
    public async Task LoadRootsAsync_PopulatesRootNodes()
    {
        await _vm.LoadRootsAsync();

        _vm.RootNodes.Count.ShouldBe(2);
        _vm.RootNodes[0].DisplayName.ShouldBe("Node1");
        _vm.RootNodes[1].DisplayName.ShouldBe("Node2");
    }

    /// <summary>Verifies that LoadRootsAsync browses with null parent.</summary>
    [Fact]
    public async Task LoadRootsAsync_BrowsesWithNullParent()
    {
        await _vm.LoadRootsAsync();

        _service.BrowseCallCount.ShouldBe(1);
        _service.LastBrowseParentNodeId.ShouldBeNull();
    }

    /// <summary>Verifies that Clear removes all root nodes.</summary>
    [Fact]
    public void Clear_RemovesAllRootNodes()
    {
        _vm.RootNodes.Add(new TreeNodeViewModel("ns=2;s=X", "X", "Object", false, _service, _dispatcher));
        _vm.Clear();

        _vm.RootNodes.ShouldBeEmpty();
    }

    /// <summary>Verifies that nodes with children have a placeholder.</summary>
    [Fact]
    public async Task LoadRootsAsync_NodeWithChildren_HasPlaceholder()
    {
        await _vm.LoadRootsAsync();

        var nodeWithChildren = _vm.RootNodes[0];
        nodeWithChildren.HasChildren.ShouldBeTrue();
        nodeWithChildren.Children.Count.ShouldBe(1);
        nodeWithChildren.Children[0].IsPlaceholder.ShouldBeTrue();
    }

    /// <summary>Verifies that nodes without children have no placeholder.</summary>
    [Fact]
    public async Task LoadRootsAsync_NodeWithoutChildren_HasNoPlaceholder()
    {
        await _vm.LoadRootsAsync();

        var leafNode = _vm.RootNodes[1];
        leafNode.HasChildren.ShouldBeFalse();
        leafNode.Children.ShouldBeEmpty();
    }

    /// <summary>Verifies that first tree node expand triggers child browse.</summary>
    [Fact]
    public async Task TreeNode_FirstExpand_TriggersChildBrowse()
    {
        _service.BrowseResults =
        [
            new BrowseResult("ns=2;s=Parent", "Parent", "Object", true)
        ];

        await _vm.LoadRootsAsync();

        // Reset browse results for child browse
        _service.BrowseResults =
        [
            new BrowseResult("ns=2;s=Child1", "Child1", "Variable", false)
        ];

        var parent = _vm.RootNodes[0];
        var initialBrowseCount = _service.BrowseCallCount;

        parent.IsExpanded = true;

        // Allow async operation to complete
        await Task.Delay(50);

        _service.BrowseCallCount.ShouldBe(initialBrowseCount + 1);
        parent.Children.Count.ShouldBe(1);
        parent.Children[0].DisplayName.ShouldBe("Child1");
    }

    /// <summary>Verifies that second tree node expand does not browse again.</summary>
    [Fact]
    public async Task TreeNode_SecondExpand_DoesNotBrowseAgain()
    {
        _service.BrowseResults =
        [
            new BrowseResult("ns=2;s=Parent", "Parent", "Object", true)
        ];

        await _vm.LoadRootsAsync();

        _service.BrowseResults =
        [
            new BrowseResult("ns=2;s=Child1", "Child1", "Variable", false)
        ];

        var parent = _vm.RootNodes[0];
        parent.IsExpanded = true;
        await Task.Delay(50);

        var browseCountAfterFirst = _service.BrowseCallCount;

        parent.IsExpanded = false;
        parent.IsExpanded = true;
        await Task.Delay(50);

        _service.BrowseCallCount.ShouldBe(browseCountAfterFirst);
    }

    /// <summary>Verifies that IsLoading transitions during browse.</summary>
    [Fact]
    public async Task TreeNode_IsLoading_TransitionsDuringBrowse()
    {
        _service.BrowseResults =
        [
            new BrowseResult("ns=2;s=Parent", "Parent", "Object", true)
        ];

        await _vm.LoadRootsAsync();

        _service.BrowseResults = [];

        var parent = _vm.RootNodes[0];
        parent.IsExpanded = true;
        await Task.Delay(50);

        // After completion, IsLoading should be false
        parent.IsLoading.ShouldBeFalse();
    }
}