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

public class GcsScoringService : BackgroundService
{
    private readonly IServiceProvider _services;
    private readonly KafkaOptions _kafkaOptions;
    private readonly ILogger<GcsScoringService> _logger;

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

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

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

        _logger.LogInformation("GcsScoringService started — consumer group: gcs-scoring");

        var guard = new PoisonPillGuard("gcs-scoring", _kafkaOptions.MaxPoisonRetries, _logger);

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

                    var evt = JsonSerializer.Deserialize<News2ObservationEvent>(
                        result.Message.Value,
                        new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;

                    using var scope = _services.CreateScope();
                    var detector = scope.ServiceProvider.GetRequiredService<GcsDetector>();

                    var outcome = await detector.ProcessObservationAsync(
                        evt.EncounterId,
                        evt.PatientId,
                        evt.ObservationCode,
                        evt.Value,
                        stoppingToken);

                    if (outcome.Outcome == GcsOutcome.EncounterNotFound)
                        _logger.LogWarning(
                            "Skipping stale observation.recorded event — encounter={Id} code={Code} offset={Offset}",
                            evt.EncounterId, evt.ObservationCode, result.Offset.Value);
                    else if (outcome.Outcome == GcsOutcome.ScoreComputed)
                        _logger.LogInformation(
                            "GCS scored via consumer — encounter={Id} total={Total} class={Class}",
                            evt.EncounterId, outcome.TotalScore, outcome.Classification);

                    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,
                        "GcsScoringService failed on topic={Topic} offset={Offset} — will retry",
                        result?.Topic, result?.Offset.Value);
                    await Task.Delay(2000, stoppingToken);
                }
            }
        }
        finally
        {
            consumer.Close();
        }
    }
}