using NexusInstaller.Models;
using NexusInstaller.Services;
using Xunit;

namespace NexusInstaller.Tests;

public sealed class InstallProgressModelTests
{
    private static IReadOnlyList<InstallPhaseItem> SampleSteps() =>
    [
        new() { Index = 0, Label = "A", Status = InstallStepStatus.Completed },
        new() { Index = 1, Label = "B", Status = InstallStepStatus.Running },
        new() { Index = 2, Label = "C", Status = InstallStepStatus.Pending },
    ];

    [Fact]
    public void ProgressPercent_counts_running_as_half_step()
    {
        var percent = InstallStepStatusRules.ProgressPercent(SampleSteps());
        Assert.Equal(50, percent);
    }

    [Fact]
    public void GlobalStatusPill_shows_installerer_while_running()
    {
        var pill = InstallStepStatusRules.GlobalStatusPill(
            isInstalling: true,
            isInstalled: false,
            isHung: false,
            SampleSteps());
        Assert.Equal("Installerer", pill);
    }

    [Fact]
    public void GlobalStatusPill_shows_timeout_when_hung()
    {
        var pill = InstallStepStatusRules.GlobalStatusPill(
            isInstalling: true,
            isInstalled: false,
            isHung: true,
            SampleSteps());
        Assert.Equal("Timeout", pill);
    }

    [Fact]
    public void GlobalStatusPill_shows_færdig_when_installed()
    {
        var steps = SampleSteps();
        foreach (var step in steps)
        {
            step.Status = InstallStepStatus.Completed;
        }

        var pill = InstallStepStatusRules.GlobalStatusPill(false, true, false, steps);
        Assert.Equal("Færdig", pill);
    }

    [Theory]
    [InlineData(InstallStepStatus.Pending, "Afventer")]
    [InlineData(InstallStepStatus.Running, "Kører")]
    [InlineData(InstallStepStatus.Completed, "Færdig")]
    [InlineData(InstallStepStatus.Failed, "Fejl")]
    [InlineData(InstallStepStatus.Blocked, "Blokeret")]
    [InlineData(InstallStepStatus.Skipped, "Sprunget over")]
    [InlineData(InstallStepStatus.Retrying, "Genprøver")]
    public void StatusDisplay_maps_all_statuses(InstallStepStatus status, string expected)
    {
        var item = new InstallPhaseItem { Index = 0, Label = "Test", Status = status };
        Assert.Equal(expected, item.StatusDisplay);
    }
}

public sealed class InstallProgressCoordinatorTests
{
    [Fact]
    public void RecentEvents_keeps_last_fifty_only()
    {
        var coordinator = new InstallProgressCoordinator(InstallerLogService.Instance);

        for (var i = 0; i < 60; i++)
        {
            coordinator.ReportInfo($"event-{i}");
        }

        Assert.Equal(InstallProgressCoordinator.MaxRecentEvents, coordinator.RecentEvents.Count);
        Assert.Contains("event-59", coordinator.RecentEvents[0].Message);
    }

    [Fact]
    public void ReportStepStarted_sets_running_and_logs_event()
    {
        var coordinator = new InstallProgressCoordinator(InstallerLogService.Instance);
        var step = new InstallPhaseItem { Index = 2, Label = "Udpakker" };

        coordinator.ReportStepStarted(step);

        Assert.Equal(InstallStepStatus.Running, step.Status);
        Assert.NotNull(step.StartedAt);
        Assert.Contains(coordinator.RecentEvents, e => e.Kind == InstallProgressEventKind.StepStarted);
    }

    [Fact]
    public void ReportStepSkipped_marks_skipped()
    {
        var coordinator = new InstallProgressCoordinator(InstallerLogService.Instance);
        var step = new InstallPhaseItem { Index = 1, Label = ".NET 8" };

        coordinator.ReportStepSkipped(step, "allerede installeret");

        Assert.Equal(InstallStepStatus.Skipped, step.Status);
        Assert.Equal("allerede installeret", step.Detail);
    }

    [Fact]
    public void ReportFileLock_adds_file_lock_event()
    {
        var coordinator = new InstallProgressCoordinator(InstallerLogService.Instance);
        coordinator.ReportFileLock("e_sqlite3.dll er låst");

        Assert.Contains(coordinator.RecentEvents, e => e.Kind == InstallProgressEventKind.FileLock);
    }
}
