using System.Text.Json;
using GerbilManagerWebAPI.Models;
using Microsoft.EntityFrameworkCore;
using WebPush;

namespace GerbilManagerWebAPI.Push
{
    /// <summary>
    /// Versendet Web-Push-Benachrichtigungen an alle gespeicherten Abos (PWA der Züchterin).
    /// Ist kein VAPID-Schlüsselpaar konfiguriert, sind die Methoden No-Ops (Push deaktiviert).
    /// Veraltete Abos (404/410) werden beim Senden entfernt.
    /// </summary>
    public class PushNotifier
    {
        private readonly IServiceScopeFactory _scopeFactory;
        private readonly ILogger<PushNotifier> _log;
        private readonly VapidDetails? _vapid;

        public PushNotifier(IConfiguration config, IServiceScopeFactory scopeFactory, ILogger<PushNotifier> log)
        {
            _scopeFactory = scopeFactory;
            _log = log;
            var subject = config["WebPush:Subject"];
            var publicKey = config["WebPush:PublicKey"];
            var privateKey = config["WebPush:PrivateKey"];
            if (!string.IsNullOrWhiteSpace(subject)
                && !string.IsNullOrWhiteSpace(publicKey)
                && !string.IsNullOrWhiteSpace(privateKey))
            {
                _vapid = new VapidDetails(subject, publicKey, privateKey);
            }
        }

        /// <summary>true, wenn Push konfiguriert ist (VAPID-Schlüssel vorhanden).</summary>
        public bool Enabled => _vapid is not null;

        public string? PublicKey => _vapid?.PublicKey;

        /// <summary>Eine Benachrichtigung an ALLE Abos senden. Fehler einzelner Abos werden geschluckt.</summary>
        public async Task NotifyAllAsync(string title, string body, string url, CancellationToken ct = default)
        {
            if (_vapid is null) return;

            using var scope = _scopeFactory.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>();
            var subs = await db.WebPushSubscriptions.ToListAsync(ct);
            if (subs.Count == 0) return;

            var client = new WebPushClient();
            var payload = JsonSerializer.Serialize(new { title, body, url });
            var stale = new List<WebPushSubscription>();
            foreach (var s in subs)
            {
                try
                {
                    var pushSub = new WebPush.PushSubscription(s.Endpoint, s.P256dh, s.Auth);
                    await client.SendNotificationAsync(pushSub, payload, _vapid);
                }
                catch (WebPushException ex) when (ex.StatusCode is System.Net.HttpStatusCode.NotFound
                                                      or System.Net.HttpStatusCode.Gone)
                {
                    stale.Add(s); // Abo abgelaufen/abgemeldet -> aufräumen
                }
                catch (Exception ex)
                {
                    _log.LogWarning(ex, "Push an {Endpoint} fehlgeschlagen", s.Endpoint);
                }
            }
            if (stale.Count > 0)
            {
                db.WebPushSubscriptions.RemoveRange(stale);
                await db.SaveChangesAsync(ct);
            }
        }
    }
}
