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
{
    [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();

    [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);
    }

    [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));
    }

    [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));
    }

    [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);
    }

    [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
        };
    }

    [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
    }
}