using System.Text.Json;
using Confluent.Kafka;
using Microsoft.Extensions.Options;

public class SepsisEngineService : BackgroundService
{
    private static readonly JsonSerializerOptions EventJsonOptions = new()
    {
        PropertyNameCaseInsensitive = true
    };

    private readonly IServiceProvider _services;
    private readonly KafkaOptions _kafkaOptions;
    private readonly ILogger<SepsisEngineService> _logger;

    public SepsisEngineService(
        IServiceProvider services,
        IOptions<KafkaOptions> kafkaOptions,
        ILogger<SepsisEngineService> logger)
    {
        _services = services;
        _kafkaOptions = kafkaOptions.Value;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var config = new ConsumerConfig
        {
            BootstrapServers = _kafkaOptions.BootstrapServers,
            GroupId = "sepsis-engine",
            AutoOffsetReset = AutoOffsetReset.Earliest,
            EnableAutoCommit = false
        }.ApplySecurity(_kafkaOptions);

        using var consumer = new ConsumerBuilder<string, string>(config).Build();
        consumer.Subscribe(_kafkaOptions.Topics.ObservationRecorded);

        _logger.LogInformation(
            "SepsisEngineService started — consumer group: sepsis-engine (qSOFA screening only)");

        var guard = new PoisonPillGuard("sepsis-engine", _kafkaOptions.MaxPoisonRetries, _logger);

        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                ConsumeResult<string, string>? result = null;
                try
                {
                    result = consumer.Consume(stoppingToken);

                    var evt = JsonSerializer.Deserialize<SepsisObservationEvent>(
                        result.Message.Value, EventJsonOptions)!;

                    using var scope = _services.CreateScope();
                    var qsofaDetector = scope.ServiceProvider.GetRequiredService<QsofaDetector>();

                    var qsofaOutcome = await qsofaDetector.ProcessObservationAsync(
                        evt.EncounterId,
                        evt.PatientId,
                        evt.ObservationCode,
                        evt.Value,
                        stoppingToken);

                    if (qsofaOutcome.Outcome == QsofaOutcome.AlertCreated)
                        _logger.LogInformation(
                            "QSOFA_SCREEN created via SepsisEngine " +
                            "— encounter={EncounterId} code={Code} value={Value}",
                            evt.EncounterId, evt.ObservationCode, evt.Value);

                    consumer.Commit(result);
                    guard.OnSuccess();
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    if (result is not null && guard.ShouldSkip(result, ex))
                    {
                        consumer.Commit(result);
                        continue;
                    }

                    _logger.LogError(ex,
                        "SepsisEngine failed on topic={Topic} offset={Offset} — will retry",
                        result?.Topic, result?.Offset.Value);
                    await Task.Delay(2000, stoppingToken);
                }
            }
        }
        finally
        {
            consumer.Close();
        }
    }
}