using SharepointToolbox.Core.Models;
using SharepointToolbox.Services;

namespace SharepointToolbox.Tests.Services;

public class BulkOperationRunnerTests
{
    [Fact]
    public async Task RunAsync_AllSucceed_ReturnsAllSuccess()
    {
        var items = new List<string> { "a", "b", "c" };
        var progress = new Progress<OperationProgress>();

        var summary = await BulkOperationRunner.RunAsync(
            items,
            (item, idx, ct) => Task.CompletedTask,
            progress,
            CancellationToken.None);

        Assert.Equal(3, summary.TotalCount);
        Assert.Equal(3, summary.SuccessCount);
        Assert.Equal(0, summary.FailedCount);
        Assert.False(summary.HasFailures);
    }

    [Fact]
    public async Task RunAsync_SomeItemsFail_ContinuesAndReportsPerItem()
    {
        var items = new List<string> { "ok1", "fail", "ok2" };
        var progress = new Progress<OperationProgress>();

        var summary = await BulkOperationRunner.RunAsync(
            items,
            (item, idx, ct) =>
            {
                if (item == "fail") throw new InvalidOperationException("Test error");
                return Task.CompletedTask;
            },
            progress,
            CancellationToken.None);

        Assert.Equal(3, summary.TotalCount);
        Assert.Equal(2, summary.SuccessCount);
        Assert.Equal(1, summary.FailedCount);
        Assert.True(summary.HasFailures);
        Assert.Contains(summary.FailedItems, r => r.ErrorMessage == "Test error");
    }

    [Fact]
    public async Task RunAsync_Cancelled_ThrowsOperationCanceled()
    {
        var items = new List<string> { "a", "b", "c" };
        var cts = new CancellationTokenSource();
        cts.Cancel();
        var progress = new Progress<OperationProgress>();

        await Assert.ThrowsAsync<OperationCanceledException>(() =>
            BulkOperationRunner.RunAsync(
                items,
                (item, idx, ct) => Task.CompletedTask,
                progress,
                cts.Token));
    }

    [Fact]
    public async Task RunAsync_CancelledMidOperation_StopsProcessing()
    {
        var items = new List<string> { "a", "b", "c", "d" };
        var cts = new CancellationTokenSource();
        var processedCount = 0;
        var progress = new Progress<OperationProgress>();

        await Assert.ThrowsAsync<OperationCanceledException>(() =>
            BulkOperationRunner.RunAsync(
                items,
                async (item, idx, ct) =>
                {
                    Interlocked.Increment(ref processedCount);
                    if (idx == 1) cts.Cancel(); // cancel after second item
                    await Task.CompletedTask;
                },
                progress,
                cts.Token));

        Assert.True(processedCount <= 3); // should not process all 4
    }

    [Fact]
    public async Task RunAsync_ReportsProgress()
    {
        var items = new List<string> { "a", "b" };
        var progressReports = new List<OperationProgress>();
        var progress = new Progress<OperationProgress>(p => progressReports.Add(p));

        await BulkOperationRunner.RunAsync(
            items,
            (item, idx, ct) => Task.CompletedTask,
            progress,
            CancellationToken.None);

        // Progress is async, give it a moment to flush
        await Task.Delay(100);
        Assert.True(progressReports.Count >= 2);
    }
}
