using System.Reflection;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services;
using SharepointToolbox.ViewModels;
using SharepointToolbox.ViewModels.Tabs;
using Xunit;

namespace SharepointToolbox.Tests.ViewModels;

/// <summary>
/// Verifies that the report filter flags (Show*) project the raw scan output
/// (<c>_allNodes</c>) into <c>Results</c> correctly: hiding a kind drops the
/// matching root nodes plus their entire subtree, preserving DFS ordering.
/// </summary>
public class StorageViewModelFilterTests
{
    public StorageViewModelFilterTests() => WeakReferenceMessenger.Default.Reset();

    private static StorageViewModel CreateVm()
    {
        var vm = new StorageViewModel(
            new Mock<IStorageService>().Object,
            new Mock<ISessionManager>().Object,
            NullLogger<FeatureViewModelBase>.Instance);
        vm.SetCurrentProfile(new TenantProfile { Name = "T", TenantUrl = "https://t", ClientId = "c" });
        return vm;
    }

    /// <summary>Inject a flat node list straight into the private _allNodes field
    /// and trigger a rebuild via toggling a Show flag.</summary>
    private static void Seed(StorageViewModel vm, List<StorageNode> flat)
    {
        var field = typeof(StorageViewModel).GetField("_allNodes",
            BindingFlags.Instance | BindingFlags.NonPublic)!;
        field.SetValue(vm, flat);
        // Toggle off+on to force RebuildFilteredResults().
        vm.ShowLibraries = false;
        vm.ShowLibraries = true;
    }

    private static List<StorageNode> MakeMixedTree() => new()
    {
        new() { Name = "Documents", Kind = StorageNodeKind.Library, IndentLevel = 0, TotalSizeBytes = 100 },
        new() { Name = "Sub",       Kind = StorageNodeKind.Library, IndentLevel = 1, TotalSizeBytes = 50  },
        new() { Name = "Preserve",  Kind = StorageNodeKind.PreservationHold, IndentLevel = 0, TotalSizeBytes = 200 },
        new() { Name = "[Recycle]", Kind = StorageNodeKind.RecycleBin,       IndentLevel = 0, TotalSizeBytes = 300 },
        new() { Name = "[Attach] L",Kind = StorageNodeKind.ListAttachments,  IndentLevel = 0, TotalSizeBytes = 75  },
    };

    [Fact]
    public void AllShowFlagsTrue_AllNodesAppear()
    {
        var vm = CreateVm();
        Seed(vm, MakeMixedTree());
        Assert.Equal(5, vm.Results.Count);
    }

    [Fact]
    public void HideRecycleBin_RemovesOnlyRecycleNode()
    {
        var vm = CreateVm();
        Seed(vm, MakeMixedTree());
        vm.ShowRecycleBin = false;
        Assert.DoesNotContain(vm.Results, n => n.Kind == StorageNodeKind.RecycleBin);
        Assert.Equal(4, vm.Results.Count);
    }

    [Fact]
    public void HidePreservationHold_RemovesPreservationNode()
    {
        var vm = CreateVm();
        Seed(vm, MakeMixedTree());
        vm.ShowPreservationHold = false;
        Assert.DoesNotContain(vm.Results, n => n.Kind == StorageNodeKind.PreservationHold);
    }

    [Fact]
    public void HideLibraries_DropsLibraryRootAndItsChildren()
    {
        var vm = CreateVm();
        Seed(vm, MakeMixedTree());
        vm.ShowLibraries = false;
        // "Documents" + "Sub" both gone — Sub's subtree dropped with its parent root.
        Assert.DoesNotContain(vm.Results, n => n.Name == "Documents");
        Assert.DoesNotContain(vm.Results, n => n.Name == "Sub");
    }

    [Fact]
    public void CombineRecycleBinStages_True_MergesStagesIntoSingleRow()
    {
        var vm = CreateVm();
        var nodes = new List<StorageNode>
        {
            new() { Name = "[Recycle Bin] First-stage",  Kind = StorageNodeKind.RecycleBin, IndentLevel = 0,
                    SiteTitle = "S1", TotalSizeBytes = 100, FileStreamSizeBytes = 100, TotalFileCount = 3 },
            new() { Name = "[Recycle Bin] Second-stage", Kind = StorageNodeKind.RecycleBin, IndentLevel = 0,
                    SiteTitle = "S1", TotalSizeBytes = 250, FileStreamSizeBytes = 250, TotalFileCount = 7 },
        };
        Seed(vm, nodes);
        vm.CombineRecycleBinStages = true;

        var bins = vm.Results.Where(n => n.Kind == StorageNodeKind.RecycleBin).ToList();
        Assert.Single(bins);
        Assert.Equal(350, bins[0].TotalSizeBytes);
        Assert.Equal(10,  bins[0].TotalFileCount);
    }

    [Fact]
    public void CombineRecycleBinStages_False_KeepsSeparateRows()
    {
        var vm = CreateVm();
        var nodes = new List<StorageNode>
        {
            new() { Name = "[Recycle Bin] First-stage",  Kind = StorageNodeKind.RecycleBin, IndentLevel = 0,
                    SiteTitle = "S1", TotalSizeBytes = 100 },
            new() { Name = "[Recycle Bin] Second-stage", Kind = StorageNodeKind.RecycleBin, IndentLevel = 0,
                    SiteTitle = "S1", TotalSizeBytes = 250 },
        };
        Seed(vm, nodes);
        vm.CombineRecycleBinStages = false;

        Assert.Equal(2, vm.Results.Count(n => n.Kind == StorageNodeKind.RecycleBin));
    }

    [Fact]
    public void HideAll_LeavesEmptyResults()
    {
        var vm = CreateVm();
        Seed(vm, MakeMixedTree());
        vm.ShowLibraries = false;
        vm.ShowHiddenLibraries = false;
        vm.ShowPreservationHold = false;
        vm.ShowListAttachments = false;
        vm.ShowRecycleBin = false;
        vm.ShowSubsites = false;
        Assert.Empty(vm.Results);
    }
}
