using ClawdDotNet.Core.Deploymentcenter.Watchdog;
using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;

namespace ClawdDotNet.Core.Tests.Deploymentcenter;

public sealed class WatchdogHeartbeatServiceTests
{
    /// <summary>Merkt sich, was gesendet wurde, ohne Netzwerk.</summary>
    private sealed class RecordingClient : IWatchdogClient
    {
        public List<(string Status, string? Message)> Heartbeats { get; } = [];
        public List<(string Kind, string? Message)> Events { get; } = [];

        public Task<WatchdogPingResult> SendHeartbeatAsync(
            InstanceHealth health, int intervalSeconds, CancellationToken ct)
        {
            Heartbeats.Add((health.Status, health.Message));
            return Task.FromResult(new WatchdogPingResult("up", []));
        }

        public Task SendEventAsync(
            string kind, string severity, string? message, object? meta, CancellationToken ct)
        {
            Events.Add((kind, message));
            return Task.CompletedTask;
        }
    }

    private sealed class FixedHealth(InstanceHealth health) : IInstanceHealthProvider
    {
        public Task<InstanceHealth> GetAsync(CancellationToken ct) => Task.FromResult(health);
    }

    private static WatchdogHeartbeatService Build(RecordingClient client) =>
        new(client, new FixedHealth(InstanceHealth.Ok("läuft")), 3600, NullLogger.Instance);

    [Fact]
    public async Task Regulaeres_Ende_meldet_stopped()
    {
        var client = new RecordingClient();
        var service = Build(client);

        service.Start();
        await service.DisposeAsync();

        client.Heartbeats[^1].Status.ShouldBe(WatchdogStatus.Stopped);
        client.Events[^1].Kind.ShouldBe(WatchdogEventKind.StoppedGraceful);
    }

    /// <summary>
    /// Beim Update ist die Instanz gleich weg, kommt aber wieder. <c>stopped</c> wäre
    /// die falsche Auskunft: Der Monitor bliebe liegen, bis jemand ihn anfasst.
    /// </summary>
    [Fact]
    public async Task Angekuendigte_Wartung_meldet_maintenance()
    {
        var client = new RecordingClient();
        var service = Build(client);

        service.Start();
        service.AnnounceMaintenance("Update auf 0.1.1 wird eingespielt.");
        await service.DisposeAsync();

        var last = client.Heartbeats[^1];
        last.Status.ShouldBe(WatchdogStatus.Maintenance);
        last.Message.ShouldContain("0.1.1");

        client.Events[^1].Kind.ShouldBe(WatchdogEventKind.MaintenanceStart);
    }

    /// <summary>Der erste Beat geht sofort raus, nicht erst nach einem vollen Intervall.</summary>
    [Fact]
    public async Task Erster_Heartbeat_kommt_sofort()
    {
        var client = new RecordingClient();
        var service = Build(client);

        service.Start();
        await service.DisposeAsync();

        // Start-Ereignis, mindestens ein Beat, dann die Abmeldung.
        client.Events[0].Kind.ShouldBe(WatchdogEventKind.Started);
        client.Heartbeats.Count.ShouldBeGreaterThanOrEqualTo(2);
        client.Heartbeats[0].Status.ShouldBe(WatchdogStatus.Ok);
    }
}
