using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;

public class ThresholdCacheLoader : IHostedService
{
    private readonly IServiceProvider _services;
    private readonly IConnectionMultiplexer _redis;
    private readonly ILogger<ThresholdCacheLoader> _logger;

    public ThresholdCacheLoader(
        IServiceProvider services,
        IConnectionMultiplexer redis,
        ILogger<ThresholdCacheLoader> logger)
    {
        _services = services;
        _redis = redis;
        _logger = logger;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        using var scope = _services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<GatewayDbContext>();
        await RefreshAsync(db, _redis, _logger, cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

    /// <summary>
    /// Reloads all gateway alert thresholds into Redis. Called on startup and after
    /// <see cref="EncounterReplicaSyncService"/> pulls thresholds from central.
    /// </summary>
    public static async Task RefreshAsync(
        GatewayDbContext db,
        IConnectionMultiplexer redis,
        ILogger logger,
        CancellationToken cancellationToken = default)
    {
        var thresholds = await db.AlertThresholds.ToListAsync(cancellationToken);
        if (thresholds.Count == 0)
        {
            logger.LogWarning("No alert thresholds in gateway DB — Redis cache not updated");
            return;
        }

        const int maxAttempts = 3;
        int[] backoffMs = [2000, 4000, 8000];

        for (var attempt = 0; attempt < maxAttempts; attempt++)
        {
            try
            {
                WriteThresholds(redis, thresholds);
                logger.LogInformation(
                    "Loaded {Count} alert thresholds into gateway Redis cache", thresholds.Count);
                return;
            }
            catch (RedisException ex)
            {
                logger.LogWarning(ex,
                    "Redis unavailable during gateway threshold cache load — attempt {Attempt}/{Max}",
                    attempt + 1, maxAttempts);

                if (attempt < maxAttempts - 1)
                    await Task.Delay(backoffMs[attempt], cancellationToken);
            }
        }

        logger.LogError(
            "Failed to load thresholds into gateway Redis after {Max} attempts — " +
            "observation ingest falls back to PostgreSQL for threshold lookups",
            maxAttempts);
    }

    private static void WriteThresholds(IConnectionMultiplexer redis, List<ReplicaAlertThreshold> thresholds)
    {
        var cache = redis.GetDatabase();
        var batch = cache.CreateBatch();

        foreach (var t in thresholds)
        {
            var json = JsonSerializer.Serialize(new ThresholdCacheEntry(
                t.ObservationCode,
                t.CriticalLow,
                t.WarningLow,
                t.WarningHigh,
                t.CriticalHigh));
            _ = batch.StringSetAsync($"threshold:{t.ObservationCode}", json);
        }

        batch.Execute();
    }
}
