using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Infrastructure.Repositories;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using Moq;
using Options = Microsoft.Extensions.Options.Options;

namespace adas_core.Test.Repositories;

[TestFixture]
[Order(1)]
[Category("Integration")]
public class AlarmRepositoryTest
{
    /// <summary>
    /// One-time test setup that initializes the alarm repository with two sample
    /// <see cref="PatientObservationAlarm"/> records by resetting the target
    /// collection and inserting the test data for use across the test fixture.
    /// </summary>
    [OneTimeSetUp]
        public async Task Init()
        {
            _optionsApiSettings = Options.Create(_apiSettings);
    
    
            var pObsAlarm1 = new PatientObservationAlarm
            {
                Id = Id,
                PatientId = PatientId,
                Code = "code1",
                CodingSystem = "SNM",
                Name = "name1",
                Time = Now,
                Value = "value1",
                SystemId = "systemId1"
            };
    
            var pObsAlarm2 = new PatientObservationAlarm
            {
                Id = ObjectId.GenerateNewId(),
                PatientId = PatientId,
                Code = "code2",
                CodingSystem = "SNM",
                Name = "name2",
                Time = Now,
                Value = "value2",
                SystemId = "systemId2"
            };
    
            await IntegrationDb.Database.DropCollectionAsync("patients_alarms");
            await IntegrationDb.Database.CreateCollectionAsync("patients_alarms");
    
            _logger = new Mock<ILogger<AlarmRepository>>();
    
            _repository = new AlarmRepository(_optionsApiSettings, IntegrationDb.Database, _logger.Object);
    
            await _repository.InsertOneAsync(pObsAlarm1);
            await _repository.InsertOneAsync(pObsAlarm2);
        }

    private AlarmRepository _repository;

    private readonly ApiSettings _apiSettings = new()
    {
        PatientsAlarms = "patients_alarms"
    };

    private IOptions<ApiSettings>? _optionsApiSettings;
    private Mock<ILogger<AlarmRepository>> _logger;

    private static readonly DateTime Now = DateTime.UtcNow;
    private static readonly ObjectId PatientId = ObjectId.GenerateNewId();
    private static readonly ObjectId Id = ObjectId.GenerateNewId();

    /// <summary>
    /// Verifies that <see cref="AlarmRepository.AggregatedPatientLastObservationsByField"/> returns an empty list when no observations match the provided field filter criteria.
    /// </summary>
    /// <param name="filter">The collection of <see cref="Field"/> objects used to filter observations; contains a single non-existent field to ensure no matches are found.</param>
    /// <returns>A task that completes after asserting the repository returns a non-null but empty result.</returns>
    [Test]
        public async Task AggregatedPatientLastObservationsByField_Not_Found_Returns_Empty_List()
        {
            var filter = new List<Field>
            {
                new()
                {
                    Name = "name100",
                    Last = 2,
                    OnlyExpired = false
                }
            };
    
            var result = await _repository.AggregatedPatientLastObservationsByField(PatientId, filter);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Empty);
        }

    /// <summary>
    /// Verifies that AggregatedPatientLastObservationsByField returns exactly one aggregated result when matching last observations are found for the specified patient and field filter.
    /// </summary>
    [Test]
        public async Task AggregatedPatientLastObservationsByField_Found_Returns_1()
        {
            var filter = new List<Field>
            {
                new()
                {
                    Name = "name1",
                    Last = 2,
                    OnlyExpired = false
                }
            };
    
            var result = await _repository.AggregatedPatientLastObservationsByField(PatientId, filter);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Count(), Is.EqualTo(1));
        }

    /// <summary>
    /// Verifies that <see cref="AlarmRepository.AggregatedPatientLastObservationsByField"/> returns a non-null collection containing an entry for each matching field in the filter when observations are found for the given patient.
    /// </summary>
    [Test]
        public async Task AggregatedPatientLastObservationsByField_Found_Returns_2()
        {
            var filter = new List<Field>
            {
                new()
                {
                    Name = "name1",
                    Last = 1,
                    OnlyExpired = false
                },
                new()
                {
                    Name = "name2",
                    Last = 1,
                    OnlyExpired = false
                }
            };
    
            var result = await _repository.AggregatedPatientLastObservationsByField(PatientId, filter);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Count(), Is.EqualTo(2));
        }

    /// <summary>
    /// Verifies that the repository returns an empty list when querying aggregated patient last observations by field with a filter that specifies OnlyExpired as true, and the matching patient observation alarm is expired.
    /// </summary>
    [Test]
        public async Task AggregatedPatientLastObservationsByField_AllExpired_Returns_Empty_List()
        {
            var expiredTime = Now.AddDays(-1); // Set a time in the past to simulate expiration
    
            var expiredObs = new PatientObservationAlarm
            {
                Id = ObjectId.GenerateNewId(),
                PatientId = PatientId,
                Code = "code3",
                CodingSystem = "SNM",
                Name = "name3",
                Time = expiredTime,
                Value = "value3",
                SystemId = "systemId3",
                Expired = true
            };
    
            await _repository.InsertOneAsync(expiredObs);
    
            var filter = new List<Field>
            {
                new()
                {
                    Name = "name3",
                    Last = 2,
                    OnlyExpired = true
                }
            };
    
            var result = await _repository.AggregatedPatientLastObservationsByField(PatientId, filter);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Empty);
        }

    /// <summary>
    /// Verifies that when multiple observations exist for the same field, the aggregation returns only the most recent observation based on the timestamp.
    /// </summary>
    [Test]
        public async Task AggregatedPatientLastObservationsByField_MultipleObservations_Returns_Most_Recent()
        {
            var oldTime = Now.AddMinutes(-10); // Set an older time
    
            var oldObs = new PatientObservationAlarm
            {
                Id = ObjectId.GenerateNewId(),
                PatientId = PatientId,
                Code = "code4",
                CodingSystem = "SNM",
                Name = "name4",
                Time = oldTime,
                Value = "value4",
                SystemId = "systemId4"
            };
    
            var newObs = new PatientObservationAlarm
            {
                Id = ObjectId.GenerateNewId(),
                PatientId = PatientId,
                Code = "code4",
                CodingSystem = "SNM",
                Name = "name4",
                Time = Now,
                Value = "value5",
                SystemId = "systemId5"
            };
    
            await _repository.InsertOneAsync(oldObs);
            await _repository.InsertOneAsync(newObs);
    
            var filter = new List<Field>
            {
                new()
                {
                    Name = "name4",
                    Last = 1,
                    OnlyExpired = false
                }
            };
    
            var result = await _repository.AggregatedPatientLastObservationsByField(PatientId, filter);
    
            Assert.That(result, Is.Not.Null);
            using (Assert.EnterMultipleScope())
            {
                
                Assert.That(result, Has.Count.EqualTo(1));
                Assert.That(result.First().Value, Is.EqualTo("value5")); // Ensure the most recent observation is returned
            };
        }

    /// <summary>
    /// Verifies that <see cref="AlarmRepository.AggregatedPatientLastObservationsByField"/> returns all of a patient's last observations when no field filter is provided.
    /// </summary>
    [Test]
        public async Task AggregatedPatientLastObservationsByField_FilterObservations_Null_Returns_All()
        {
            var result = await _repository.AggregatedPatientLastObservationsByField(PatientId);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Not.Empty); // Ensure it returns all observations for the patient
        }
}