using ErsatzTV.Application.Health;
using ErsatzTV.Core.Api.Health;
using ErsatzTV.Core.Health;
using LanguageExt;
using NSubstitute;
using NUnit.Framework;
using Shouldly;

namespace ErsatzTV.Tests.Application.Health;

[TestFixture]
public class GetAllHealthCheckResultsForApiHandlerTests
{
    private IHealthCheckService _healthCheckService = null!;
    private GetAllHealthCheckResultsForApiHandler _handler = null!;

    [SetUp]
    public void SetUp()
    {
        _healthCheckService = Substitute.For<IHealthCheckService>();
        _handler = new GetAllHealthCheckResultsForApiHandler(_healthCheckService);
    }

    [Test]
    public async Task Should_Map_Status_Codes_To_Lowercase_Strings()
    {
        var results = new List<HealthCheckResult>
        {
            new("Pass Check", HealthCheckStatus.Pass, "all good", "ok", Option<HealthCheckLink>.None),
            new("Fail Check", HealthCheckStatus.Fail, "broken", "bad", Option<HealthCheckLink>.None),
            new("Warn Check", HealthCheckStatus.Warning, "watch out", "warn", Option<HealthCheckLink>.None),
            new("Info Check", HealthCheckStatus.Info, "fyi", "info", Option<HealthCheckLink>.None)
        };

        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(results);

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        response.Count.ShouldBe(4);
        response.Select(r => r.Status).ShouldBe(["pass", "fail", "warn", "info"]);
    }

    [Test]
    public async Task Should_Filter_Out_NotApplicable_Results()
    {
        var results = new List<HealthCheckResult>
        {
            new("Pass Check", HealthCheckStatus.Pass, "all good", "ok", Option<HealthCheckLink>.None),
            new("NA Check", HealthCheckStatus.NotApplicable, "skip", "skip", Option<HealthCheckLink>.None)
        };

        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(results);

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        response.Count.ShouldBe(1);
        response[0].Title.ShouldBe("Pass Check");
    }

    [Test]
    public async Task Should_Include_ExternalDoc_Remediation_When_Present()
    {
        var results = new List<HealthCheckResult>
        {
            new(
                "Linked Check",
                HealthCheckStatus.Warning,
                "detail message",
                "brief",
                Option<HealthCheckLink>.Some(HealthCheckLink.ExternalDoc("https://example.com/docs")))
        };

        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(results);

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        // deprecated Link mirrors the remediation target for back-compat
        response[0].Link.ShouldBe("https://example.com/docs");
        response[0].Detail.ShouldBe("detail message");
        response[0].Brief.ShouldBe("brief");
        response[0].Remediation.ShouldNotBeNull();
        response[0].Remediation!.Kind.ShouldBe("ExternalDoc");
        response[0].Remediation!.Target.ShouldBe("https://example.com/docs");
    }

    [Test]
    public async Task Should_Include_AppRoute_Remediation_When_Present()
    {
        var results = new List<HealthCheckResult>
        {
            new(
                "Routed Check",
                HealthCheckStatus.Warning,
                "detail message",
                "brief",
                Option<HealthCheckLink>.Some(HealthCheckLink.AppRoute("/app/trash")))
        };

        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(results);

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        response[0].Link.ShouldBe("/app/trash");
        response[0].Remediation.ShouldNotBeNull();
        response[0].Remediation!.Kind.ShouldBe("AppRoute");
        response[0].Remediation!.Target.ShouldBe("/app/trash");
    }

    [Test]
    public async Task Should_Have_Null_Link_And_Remediation_When_Absent()
    {
        var results = new List<HealthCheckResult>
        {
            new("No Link Check", HealthCheckStatus.Pass, "detail", "brief", Option<HealthCheckLink>.None)
        };

        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(results);

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        response[0].Link.ShouldBeNull();
        response[0].Remediation.ShouldBeNull();
    }

    [Test]
    public async Task Should_Map_Empty_BriefMessage_To_Null_Brief()
    {
        var results = new List<HealthCheckResult>
        {
            new("Blank Brief", HealthCheckStatus.Pass, "detail", string.Empty, Option<HealthCheckLink>.None)
        };

        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>()).Returns(results);

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        response[0].Brief.ShouldBeNull();
    }

    [Test]
    public async Task Should_Return_Empty_List_On_Cancellation()
    {
        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>())
            .Returns<Task<List<HealthCheckResult>>>(_ => throw new TaskCanceledException());

        List<HealthCheckResponseModel> response =
            await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        response.ShouldBeEmpty();
    }

    [Test]
    public async Task Should_Not_Force_Refresh_By_Default()
    {
        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>())
            .Returns(new List<HealthCheckResult>());

        await _handler.Handle(new GetAllHealthCheckResultsForApi(), CancellationToken.None);

        await _healthCheckService.Received(1).PerformHealthChecks(false, Arg.Any<CancellationToken>());
    }

    [Test]
    public async Task Should_Force_Refresh_When_Requested()
    {
        _healthCheckService.PerformHealthChecks(Arg.Any<bool>(), Arg.Any<CancellationToken>())
            .Returns(new List<HealthCheckResult>());

        await _handler.Handle(new GetAllHealthCheckResultsForApi(Refresh: true), CancellationToken.None);

        await _healthCheckService.Received(1).PerformHealthChecks(true, Arg.Any<CancellationToken>());
    }
}
