using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

public sealed class DisconnectedMonitorsCheck
{
    public static readonly ReconciliationCheckType CheckType = ReconciliationCheckType.ActiveInpatientNoObservation;

    private readonly AppDbContext _db;
    private readonly ReconciliationPublisher _publisher;
    private readonly ReconciliationJobOptions _opts;
    private readonly ILogger<DisconnectedMonitorsCheck> _logger;

    public DisconnectedMonitorsCheck(
        AppDbContext db,
        ReconciliationPublisher publisher,
        IOptions<ReconciliationJobOptions> opts,
        ILogger<DisconnectedMonitorsCheck> logger)
    {
        _db        = db;
        _publisher = publisher;
        _opts      = opts.Value;
        _logger    = logger;
    }

    public async Task<int> RunAsync(CancellationToken ct)
    {
        var cutoff = DateTimeOffset.UtcNow
            .AddHours(-_opts.NoObservationThresholdHours);

        // Limitation: this check detects absence of recorded observations, not absence
        // of actual clinical monitoring. A nurse who took manual vitals but did not
        // enter them into the system would still trigger a reconciliation alert.
        // This is a known gap; the check errs on the side of false positives.
        var candidates = await _db.Encounters
            .Where(e => e.Status       == EncounterStatus.Active
                     && e.EncounterType == EncounterType.Inpatient)
            .Select(e => new
            {
                EncounterId       = e.Id,
                e.PatientId,
                LastObservationAt = e.Observations
                    .OrderByDescending(o => o.RecordedAt)
                    .Select(o => (DateTimeOffset?)o.RecordedAt)
                    .FirstOrDefault(),
            })
            .ToListAsync(ct);

        var stale = candidates
            .Where(e => e.LastObservationAt == null || e.LastObservationAt < cutoff)
            .ToList();

        if (!stale.Any())
        {
            _logger.LogDebug("[RECONCILIATION] Check3 — all active inpatients have recent observations");
            return 0;
        }

        var inserted = 0;
        foreach (var enc in stale)
        {
            var alreadyOpen = await _db.ReconciliationAlerts
                .AnyAsync(r => r.CheckType   == CheckType
                            && r.EncounterId == enc.EncounterId
                            && r.ResolvedAt  == null, ct);
            if (alreadyOpen) continue;

            var lastObs = enc.LastObservationAt.HasValue
                ? $"last observation {enc.LastObservationAt:u}"
                : "no observations ever recorded";
            var details = $"Active inpatient encounter {enc.EncounterId} has no recent observations "
                        + $"({lastObs}). Monitor may be disconnected.";

            var rec = new ReconciliationAlert
            {
                Id          = Guid.NewGuid(),
                CheckType   = CheckType,  // ReconciliationCheckType enum
                EncounterId = enc.EncounterId,
                PatientId   = enc.PatientId,
                Details     = details,
                CreatedAt   = DateTimeOffset.UtcNow,
            };

            _db.ReconciliationAlerts.Add(rec);
            await _db.SaveChangesAsync(ct);
            await _publisher.PublishAsync(rec, ct);

            _logger.LogWarning(
                "[RECONCILIATION] {CheckType} — encounter {EncounterId}: {LastObs}",
                CheckType, enc.EncounterId, lastObs);

            inserted++;
        }

        return inserted;
    }
}