using ClawdDotNet.Core.Accounting;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Deploymentcenter.Watchdog;
using Shouldly;

namespace ClawdDotNet.Core.Tests.Deploymentcenter;

public sealed class InstanceHealthProviderTests
{
    /// <summary>Liefert einen festen Tagesverbrauch, ohne DB.</summary>
    private sealed class FakeUsage(DailyUsage daily) : IUsageRepository
    {
        public Task RecordAsync(RunUsage usage, CancellationToken ct) => Task.CompletedTask;

        public Task<DailyUsage> GetDailyAsync(DateOnly date, string agentId, CancellationToken ct)
            => Task.FromResult(daily);

        public Task<IReadOnlyList<RunUsage>> GetRecentAsync(int limit, CancellationToken ct)
            => Task.FromResult((IReadOnlyList<RunUsage>)Array.Empty<RunUsage>());

        public Task<int> PurgeBeforeAsync(DateOnly date, CancellationToken ct) => Task.FromResult(0);
    }

    private static DailyUsage Usage(decimal cost, int tokens = 0) =>
        new(DateOnly.FromDateTime(DateTime.Now), tokens, 0, cost, true, 1);

    [Fact]
    public async Task Ohne_API_Key_meldet_error()
    {
        var provider = new InstanceHealthProvider(
            "Test", agentsEnabled: false, InstanceBudget.Unlimited,
            usage: null, () => 2, () => 0);

        var health = await provider.GetAsync(default);

        health.Status.ShouldBe(WatchdogStatus.Error);
        health.Metrics["agentCount"].ShouldBe(2);
        health.Checks["agents"].Ok.ShouldBeFalse();
    }

    [Fact]
    public async Task Erschoepftes_Kostenbudget_meldet_warning()
    {
        var budget = new InstanceBudget { DailyCostUsd = 5m };
        var provider = new InstanceHealthProvider(
            "Test", agentsEnabled: true, budget,
            new FakeUsage(Usage(cost: 5m)), () => 1, () => 0);

        var health = await provider.GetAsync(default);

        health.Status.ShouldBe(WatchdogStatus.Warning);
        health.Checks["budget"].Ok.ShouldBeFalse();
        health.Message.ShouldNotBeNull();
    }

    /// <summary>
    /// Ein Heartbeat beweist nur, dass ein Faden läuft. Steht der Scanner, arbeitet die
    /// Instanz nichts mehr ab — und genau das soll die Prüfung sichtbar machen.
    /// </summary>
    [Fact]
    public async Task Stehender_Scanner_meldet_warning()
    {
        var provider = new InstanceHealthProvider(
            "Test", agentsEnabled: true, InstanceBudget.Unlimited,
            new FakeUsage(Usage(cost: 0m)), () => 1, () => 0,
            schedulerRunning: () => false);

        var health = await provider.GetAsync(default);

        health.Status.ShouldBe(WatchdogStatus.Warning);
        health.Checks["scheduler"].Ok.ShouldBeFalse();
    }

    [Fact]
    public async Task Normalbetrieb_meldet_ok_mit_Metriken()
    {
        var provider = new InstanceHealthProvider(
            "Test", agentsEnabled: true, InstanceBudget.Unlimited,
            new FakeUsage(Usage(cost: 0.5m, tokens: 1200)), () => 3, () => 1,
            schedulerRunning: () => true);

        var health = await provider.GetAsync(default);

        health.Status.ShouldBe(WatchdogStatus.Ok);
        health.Metrics["runningChats"].ShouldBe(1);
        health.Metrics.Keys.ShouldContain("todayCostUsd");
        health.Metrics.Keys.ShouldContain("todayTokens");
        health.Checks.Values.ShouldAllBe(c => c.Ok);
    }

    /// <summary>
    /// Metriken werden serverseitig nur ausgewertet, wenn sie Zahlen sind — alles andere
    /// verwirft der Verlauf stillschweigend. Beschreibendes gehört in die Meldung.
    /// </summary>
    [Fact]
    public async Task Instanzname_steht_in_der_Meldung_nicht_in_den_Metriken()
    {
        var provider = new InstanceHealthProvider(
            "Produktion", agentsEnabled: true, InstanceBudget.Unlimited,
            usage: null, () => 1, () => 0);

        var health = await provider.GetAsync(default);

        health.Message.ShouldContain("Produktion");
        health.Metrics.Keys.ShouldNotContain("instanceName");
    }
}
