using System.Collections.ObjectModel;
using System.Reflection;
using CommunityToolkit.Mvvm.Messaging;
using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using SharepointToolbox.Core.Messages;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services;
using SharepointToolbox.ViewModels;
using SharepointToolbox.ViewModels.Tabs;

namespace SharepointToolbox.Tests.ViewModels;

/// <summary>
/// Unit tests for StorageViewModel chart functionality (Phase 09 Plan 04).
/// Verifies: chart series from metrics, bar series structure, donut/bar toggle,
/// top-10 + Other aggregation, no-Other for <=10, tenant switch cleanup, empty data.
/// Uses reflection to set FileTypeMetrics directly, bypassing ClientContext dependency.
/// </summary>
public class StorageViewModelChartTests
{
    public StorageViewModelChartTests()
    {
        WeakReferenceMessenger.Default.Reset();
    }

    // -- Helper factories --------------------------------------------------------

    private static StorageViewModel CreateViewModel()
    {
        var mockStorage = new Mock<IStorageService>();
        var mockSession = new Mock<ISessionManager>();

        var vm = new StorageViewModel(
            mockStorage.Object,
            mockSession.Object,
            NullLogger<FeatureViewModelBase>.Instance);

        vm.SetCurrentProfile(new TenantProfile
        {
            Name = "Test",
            TenantUrl = "https://test.sharepoint.com",
            ClientId = "test-id"
        });

        return vm;
    }

    /// <summary>
    /// Sets FileTypeMetrics via the property (private setter) using reflection,
    /// which also triggers UpdateChartSeries.
    /// </summary>
    private static void SetFileTypeMetrics(StorageViewModel vm, IList<FileTypeMetric> metrics)
    {
        var prop = typeof(StorageViewModel).GetProperty(
            nameof(StorageViewModel.FileTypeMetrics),
            BindingFlags.Public | BindingFlags.Instance);
        prop!.SetValue(vm, new ObservableCollection<FileTypeMetric>(metrics));
    }

    private static List<FileTypeMetric> MakeMetrics(int count)
    {
        var extensions = new[]
        {
            ".docx", ".pdf", ".xlsx", ".pptx", ".jpg",
            ".png", ".mp4", ".zip", ".csv", ".html",
            ".txt", ".json", ".xml", ".msg", ".eml"
        };

        var metrics = new List<FileTypeMetric>();
        for (int i = 0; i < count; i++)
        {
            string ext = i < extensions.Length ? extensions[i] : $".ext{i}";
            metrics.Add(new FileTypeMetric(ext, (count - i) * 1024L * 1024, (count - i) * 10));
        }
        return metrics;
    }

    // -- Test 1: Chart series populated from metrics -----------------------------

    [Fact]
    public void After_setting_metrics_HasChartData_is_true_and_PieChartSeries_has_entries()
    {
        var vm = CreateViewModel();
        var metrics = MakeMetrics(5);

        SetFileTypeMetrics(vm, metrics);

        Assert.True(vm.HasChartData);
        Assert.NotEmpty(vm.PieChartSeries);
        Assert.Equal(5, vm.PieChartSeries.Count());
    }

    // -- Test 2: Bar series has one ColumnSeries with correct value count --------

    [Fact]
    public void After_setting_metrics_BarChartSeries_has_one_ColumnSeries_with_matching_values()
    {
        var vm = CreateViewModel();
        var metrics = MakeMetrics(5);

        SetFileTypeMetrics(vm, metrics);

        var barSeries = vm.BarChartSeries.ToList();
        Assert.Single(barSeries);

        var columnSeries = Assert.IsType<ColumnSeries<long>>(barSeries[0]);
        Assert.Equal(5, columnSeries.Values!.Count());
    }

    // -- Test 3: Toggle IsDonutChart changes PieChartSeries InnerRadius ----------

    [Fact]
    public void Toggle_IsDonutChart_changes_PieChartSeries_InnerRadius()
    {
        var vm = CreateViewModel();
        var metrics = MakeMetrics(3);

        SetFileTypeMetrics(vm, metrics);

        // Initially IsDonutChart=true => InnerRadius=50
        var pieBefore = vm.PieChartSeries.Cast<PieSeries<double>>().ToList();
        Assert.All(pieBefore, s => Assert.Equal(50, s.InnerRadius));

        // Toggle to bar (not donut) => InnerRadius=0
        vm.IsDonutChart = false;

        var pieAfter = vm.PieChartSeries.Cast<PieSeries<double>>().ToList();
        Assert.All(pieAfter, s => Assert.Equal(0, s.InnerRadius));
    }

    // -- Test 4: More than 10 file types => 11 entries (10 + Other) --------------

    [Fact]
    public void More_than_10_metrics_produces_11_series_entries_with_Other()
    {
        var vm = CreateViewModel();
        var metrics = MakeMetrics(15);

        SetFileTypeMetrics(vm, metrics);

        // Pie series: 10 real + 1 "Other" = 11
        Assert.Equal(11, vm.PieChartSeries.Count());

        // Last pie entry should be named "OTHER" (DisplayLabel uppercases extension)
        var lastPie = vm.PieChartSeries.Last();
        Assert.Equal("OTHER", lastPie.Name);

        // Bar series column should have 11 values
        var columnSeries = Assert.IsType<ColumnSeries<long>>(vm.BarChartSeries.First());
        Assert.Equal(11, columnSeries.Values!.Count());

        // X-axis should have 11 labels
        Assert.Equal(11, vm.BarXAxes[0].Labels!.Count);
    }

    // -- Test 5: 10 or fewer file types => no "Other" entry ----------------------

    [Fact]
    public void Ten_or_fewer_metrics_produces_no_Other_entry()
    {
        var vm = CreateViewModel();
        var metrics = MakeMetrics(10);

        SetFileTypeMetrics(vm, metrics);

        Assert.Equal(10, vm.PieChartSeries.Count());

        // No entry named "OTHER" (DisplayLabel uppercases)
        Assert.DoesNotContain(vm.PieChartSeries, s => s.Name == "OTHER");
    }

    // -- Test 6: Tenant switch clears chart data ---------------------------------

    [Fact]
    public void OnTenantSwitched_clears_FileTypeMetrics_and_HasChartData_is_false()
    {
        var vm = CreateViewModel();
        var metrics = MakeMetrics(5);

        SetFileTypeMetrics(vm, metrics);
        Assert.True(vm.HasChartData);

        // Act: send TenantSwitchedMessage
        var newProfile = new TenantProfile
        {
            Name = "NewTenant",
            TenantUrl = "https://newtenant.sharepoint.com",
            ClientId = "new-id"
        };
        WeakReferenceMessenger.Default.Send(new TenantSwitchedMessage(newProfile));

        Assert.False(vm.HasChartData);
        Assert.Empty(vm.FileTypeMetrics);
        Assert.Empty(vm.PieChartSeries);
        Assert.Empty(vm.BarChartSeries);
    }

    // -- Test 7: Empty metrics => HasChartData false, series empty ---------------

    [Fact]
    public void Empty_metrics_yields_HasChartData_false_and_empty_series()
    {
        var vm = CreateViewModel();

        SetFileTypeMetrics(vm, new List<FileTypeMetric>());

        Assert.False(vm.HasChartData);
        Assert.Empty(vm.PieChartSeries);
        Assert.Empty(vm.BarChartSeries);
        Assert.Empty(vm.BarXAxes);
        Assert.Empty(vm.BarYAxes);
    }
}
