using Moq;
using SilverOS.Welcome.Core.Apply;
using SilverOS.Welcome.Core.Apps;
using SilverOS.Welcome.Core.Flavours;
using Xunit;

public class ApplyServiceTests
{
    private static Mock<IAppInstaller> NoApps()
    {
        var installer = new Mock<IAppInstaller>();
        installer.Setup(i => i.InstallAsync(It.IsAny<IReadOnlyList<AppCatalogEntry>>(),
                It.IsAny<IProgress<ApplyProgress>>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(Array.Empty<AppInstallResult>());
        return installer;
    }

    private static FlavourManifest Flavour() =>
        new() { Id = "daily-driver", Hardening = new HardeningSpec { Modules = new[] { "00" } } };

    [Fact]
    public async Task Runs_apps_then_bitlocker_when_pin_supplied()
    {
        var order = new List<string>();
        var run = new Mock<IProcessRunner>();
        run.Setup(r => r.RunAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
           .ReturnsAsync(new ProcessResult(0, "", ""));
        var bl = new Mock<IBitLockerService>();
        bl.Setup(b => b.EnableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
          .Callback(() => order.Add("bitlocker")).Returns(Task.CompletedTask);
        var installer = NoApps();
        installer.Setup(i => i.InstallAsync(It.IsAny<IReadOnlyList<AppCatalogEntry>>(),
                It.IsAny<IProgress<ApplyProgress>>(), It.IsAny<CancellationToken>()))
            .Callback(() => order.Add("apps")).ReturnsAsync(Array.Empty<AppInstallResult>());

        var sut = new ApplyService(bl.Object, installer.Object);
        var req = new ApplyRequest(Flavour(), "123456", System.Array.Empty<AppCatalogEntry>());
        var progress = new List<string>();

        await sut.RunAsync(req, new Progress<ApplyProgress>(p => progress.Add(p.Stage)));

        Assert.Equal(new[] { "apps", "bitlocker" }, order);
        Assert.Contains("Installing apps", progress);
        Assert.Contains("Done", progress);
    }

    [Fact]
    public async Task Empty_pin_skips_bitlocker()
    {
        var order = new List<string>();
        var run = new Mock<IProcessRunner>();
        run.Setup(r => r.RunAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
           .ReturnsAsync(new ProcessResult(0, "", ""));
        var bl = new Mock<IBitLockerService>();
        bl.Setup(b => b.EnableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
          .Callback(() => order.Add("bitlocker")).Returns(Task.CompletedTask);
        var installer = NoApps();
        installer.Setup(i => i.InstallAsync(It.IsAny<IReadOnlyList<AppCatalogEntry>>(),
                It.IsAny<IProgress<ApplyProgress>>(), It.IsAny<CancellationToken>()))
            .Callback(() => order.Add("apps")).ReturnsAsync(Array.Empty<AppInstallResult>());

        var sut = new ApplyService(bl.Object, installer.Object);
        var req = new ApplyRequest(Flavour(), "", System.Array.Empty<AppCatalogEntry>());

        await sut.RunAsync(req, new Progress<ApplyProgress>(_ => { }));

        Assert.Equal(new[] { "apps" }, order);
        bl.Verify(b => b.EnableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
    }

    [Fact]
    public async Task Installs_the_requested_apps()
    {
        var run = new Mock<IProcessRunner>();
        run.Setup(r => r.RunAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
           .ReturnsAsync(new ProcessResult(0, "", ""));
        var bl = new Mock<IBitLockerService>();
        var installer = NoApps();
        var apps = new[] { new AppCatalogEntry { Id = "firefox", Name = "Firefox" } };

        var sut = new ApplyService(bl.Object, installer.Object);
        var req = new ApplyRequest(Flavour(), "123456", apps);

        await sut.RunAsync(req, new Progress<ApplyProgress>(_ => { }));

        installer.Verify(i => i.InstallAsync(apps, It.IsAny<IProgress<ApplyProgress>>(),
            It.IsAny<CancellationToken>()), Times.Once);
        bl.Verify(b => b.EnableAsync("123456", It.IsAny<CancellationToken>()), Times.Once);
    }
}
