using Microsoft.Extensions.Logging;

namespace ClawdDotNet.App.Services;

/// <summary>
/// Prüft die Lizenz im laufenden Betrieb nach.
///
/// <para>Ohne das wirkt ein Widerruf erst beim nächsten Start — bei einer Anwendung, die
/// als Dienst wochenlang läuft, ist das praktisch nie. Der Takt ist bewusst grob (alle
/// zwölf Stunden): Es geht um einen Notausschalter, nicht um eine Zugangskontrolle pro
/// Klick.</para>
///
/// <para><b>Nur ein Urteil zählt.</b> Ein Netzproblem, eine Drosselung oder eine
/// abgelaufene Offline-Frist beenden nichts — das SDK meldet solche Fälle als
/// <c>IsTransient</c>, und ein Serverausfall darf nicht alle laufenden Instanzen
/// mitnehmen. Der verschlüsselte Zwischenspeicher trägt über solche Lücken hinweg.</para>
/// </summary>
public sealed class LicenseWatch : IAsyncDisposable
{
    private static readonly TimeSpan DefaultInterval = TimeSpan.FromHours(12);

    private readonly LicenseGate _gate;
    private readonly ILogger _logger;
    private readonly TimeSpan _interval;

    private CancellationTokenSource? _cts;
    private Task? _loop;

    /// <summary>
    /// Die Lizenz gilt nicht mehr. Der Aufrufer beendet die Anwendung — geordnet, aber
    /// ohne Rückfrage; der übergebene Text erklärt den Grund.
    /// </summary>
    public event Func<string, Task>? Revoked;

    public LicenseWatch(LicenseGate gate, ILogger logger, TimeSpan? interval = null)
    {
        _gate = gate;
        _logger = logger;
        _interval = interval ?? DefaultInterval;
    }

    public void Start()
    {
        if (_loop is { IsCompleted: false })
            return;

        _cts = new CancellationTokenSource();
        _loop = RunAsync(_cts.Token);
    }

    private async Task RunAsync(CancellationToken ct)
    {
        try
        {
            using var timer = new PeriodicTimer(_interval);

            while (await timer.WaitForNextTickAsync(ct).ConfigureAwait(false))
            {
                var result = await _gate.RevalidateAsync(ct).ConfigureAwait(false);

                if (result.IsValid)
                    continue;

                if (result.IsTransient)
                {
                    _logger.LogInformation(
                        "Lizenz-Nachprüfung ohne Ergebnis ({Status}) – Betrieb läuft weiter.",
                        result.Status);
                    continue;
                }

                _logger.LogWarning("Lizenz gilt nicht mehr ({Status}) – Instanz wird beendet.",
                    result.Status);

                if (Revoked is { } handler)
                {
                    await handler($"Die Lizenz ist nicht mehr gültig ({result.Status}). "
                                  + "ClawdDotNet wird beendet.").ConfigureAwait(false);
                }

                return;
            }
        }
        catch (OperationCanceledException)
        {
            // Regulärer Stopp.
        }
        catch (Exception ex)
        {
            // Die Nachprüfung darf den Betrieb nicht mitnehmen.
            _logger.LogWarning(ex, "Lizenz-Nachprüfung abgebrochen.");
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (_cts is null)
            return;

        await _cts.CancelAsync().ConfigureAwait(false);

        if (_loop is not null)
        {
            try { await _loop.ConfigureAwait(false); }
            catch (OperationCanceledException) { /* erwartet */ }
        }

        _cts.Dispose();
    }
}
