using adas_core.Application.Customizations.HUVH.UCIA;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Moq;
using Options = Microsoft.Extensions.Options.Options;

namespace adas_core.Test.Customizations.HUVH.UCIA;

[TestFixture]
[Order(2)]
public class CalculatedObservationsTest
{
    /// <summary>
    /// Initializes the test environment by creating mock instances of the observation, medicine, and treatment services,
    /// configuring a service provider with these dependencies, and instantiating the <see cref="CalculatedObservations"/>
    /// class under test.
    /// </summary>
    [SetUp]
        public void Setup()
        {
            _observationServiceMock = new Mock<IObservationService>();
    
            _medicineServiceMock = new Mock<IMedicineService>();
    
            _treatmentServiceMock = new Mock<ITreatmentService>();
    
            _logger = new Mock<ILogger<CalculatedObservations>>();
    
            _optionsApiSettings = Options.Create(_apiSettings);
    
            var serviceCollection = new ServiceCollection();
            //serviceCollection.AddSingleton(_observationServiceMock.Object);
            serviceCollection.AddSingleton(new Lazy<IObservationService>(() => _observationServiceMock.Object));
            serviceCollection.AddSingleton(new Lazy<IMedicineService>(() => _medicineServiceMock.Object));
            serviceCollection.AddSingleton(new Lazy<ITreatmentService>(() => _treatmentServiceMock.Object));
    
            serviceCollection.AddSingleton(_optionsApiSettings);
            serviceCollection.AddSingleton(_logger.Object);
    
            var serviceProvider = serviceCollection.BuildServiceProvider();
    
            _calculatedObservations = new CalculatedObservations(serviceProvider);
        }

    private CalculatedObservations _calculatedObservations;
    private Mock<IMedicineService> _medicineServiceMock;
    private Mock<IObservationService> _observationServiceMock;
    private Mock<ITreatmentService> _treatmentServiceMock;
    private Mock<ILogger<CalculatedObservations>> _logger;

    private readonly ApiSettings _apiSettings = new()
    {
        ConfigObservation = new ConfigObservationSettings
        {
            IgnoreUnknownObservation = false
        }
    };

    private IOptions<ApiSettings> _optionsApiSettings;

    //Mandar observación 'SOBRESEDACIÓN'
    //cuando:
    //(RASS =-4 o RASS =-5) y
    //PSI < 25 y
    //TS > 5 y
    //(Propofol > 3 ó Midazolam > 0,05 ó Isoflorano > 10)

    /// <summary>
    /// Verifies that an over-sedation observation is inserted when the patient meets the clinical criteria:
    /// a RASS observation with a sufficiently low score, recent PSI and TS observations, and an active treatment
    /// (e.g., Propofol) with a minimum requested give amount that triggers the over-sedation condition.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Insert_Observation_When_Conditions_Met()
        {
            var patientId = ObjectId.GenerateNewId();
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 };
            var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 };
            var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "PSI", "TS" }))
                .ReturnsAsync([psiObservation, tsObservation]);
    
            _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId))
                .ReturnsAsync(new List<PatientTreatment>
                    { new() { RequestedGiveCodes = [new Code { Text = "Propofol" }], RequestedGiveAmountMinimum = 4 } });
    
            await _calculatedObservations.Map(rassObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Once);
        }

    /// <summary>
    /// Verifies that no over-sedation observation is inserted when the patient's RASS score is not -4 or -5, ensuring the over-sedation calculation only applies for those specific values.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_RASS_Is_Not_Negative4_Or_Negative5()
        {
            var patientId = ObjectId.GenerateNewId();
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -3 };
    
            await _calculatedObservations.Map(rassObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never);
        }

    /// <summary>
    /// Verifies that no observation is inserted when calculating over-sedation and the patient's PSI (Sedation Intensity Index) is 25 or higher, which indicates the threshold above which the over-sedation mapping should not produce an observation.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_PSI_Is_25_Or_Higher()
        {
            var patientId = ObjectId.GenerateNewId();
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 };
    
            var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 25 };
            var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "PSI", "TS" }))
                .ReturnsAsync([psiObservation, tsObservation]);
    
            await _calculatedObservations.Map(rassObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never);
        }

    /// <summary>
    /// Verifies that the over-sedation calculation does not insert an observation when the patient's TS (Tidal Spontaneous) value is 5 or lower, even with a low RASS score and an elevated PSI value.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_TS_Is_5_Or_Lower()
        {
            var patientId = ObjectId.GenerateNewId();
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 };
    
            var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 };
            var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 5 };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "PSI", "TS" }))
                .ReturnsAsync([psiObservation, tsObservation]);
    
            await _calculatedObservations.Map(rassObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never);
        }

    /// <summary>
    /// Verifies that no observation is inserted when the patient has no active treatments that exceed the configured threshold during the over-sedation calculation.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_No_Treatments_Exceed_Threshold()
        {
            var patientId = ObjectId.GenerateNewId();
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 };
    
            var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 };
            var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "PSI", "TS" }))
                .ReturnsAsync([psiObservation, tsObservation]);
    
            _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId))
                .ReturnsAsync(new List<PatientTreatment>()); // No hay tratamientos activos que superen el umbral
    
            await _calculatedObservations.Map(rassObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never);
        }

    /// <summary>
    /// Verifies that an over-sedation observation is inserted when the calculation is triggered by a PSI observation
    /// and the patient's last RASS and TS observations, combined with active treatment data, meet the triggering criteria.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Insert_Observation_When_Triggered_By_PSI_Observation()
        {
            var patientId = ObjectId.GenerateNewId();
            var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 };
    
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 };
            var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "RASS", "TS" }))
                .ReturnsAsync([rassObservation, tsObservation]);
    
            _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId))
                .ReturnsAsync(new List<PatientTreatment>
                    { new() { RequestedGiveCodes = [new Code { Text = "Propofol" }], RequestedGiveAmountMinimum = 4 } });
    
            await _calculatedObservations.Map(psiObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Once);
        }

    /// <summary>
    /// Verifies that the over-sedation calculation logic inserts a new observation when a triggering TS (Twitch Score) observation is received, given the presence of related RASS and PSI observations and an active treatment (e.g., Propofol) meeting the required minimum amount threshold.
    /// </summary>
    [Test]
        public async Task CalculateOverSedation_Should_Insert_Observation_When_Triggered_By_TS_Observation()
        {
            var patientId = ObjectId.GenerateNewId();
            var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 };
    
            var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 };
            var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "RASS", "PSI" }))
                .ReturnsAsync([rassObservation, psiObservation]);
    
            _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId))
                .ReturnsAsync(new List<PatientTreatment>
                    { new() { RequestedGiveCodes = [new Code { Text = "Propofol" }], RequestedGiveAmountMinimum = 4 } });
    
            await _calculatedObservations.Map(tsObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Once);
        }

    //- Si llega valor de PI pero NO COMPL, el tipo de ventilación es VMNI
    //- Si llega valor de PS pero no llega COMPL, el tipo de ventilación es VMNI
    //- Si llega COMPL, el tipo de ventilación es VMI
    //- Si llega Flujo, leer el tipo de ventilación de CCC(CNAF o CTAF)


    /// <summary>
    /// Verifies that when an Inspiratory Pressure (PI) observation is processed and no Compliancia (COMPL) observation is found for the patient, the ventilation mode is set to "VMNI".
    /// </summary>
    [Test]
        public async Task CalculateVentilationMode_Should_Set_VMNI_When_PI_PS_Arrives_Without_COMPL()
        {
            var patientId = ObjectId.GenerateNewId();
            var piObservation = new PatientObservation { PatientId = patientId, Name = "Inspiratory_Pressure" };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "Compliancia" }))
                .ReturnsAsync([]); // No hay COMPL
    
            await _calculatedObservations.Map(piObservation);
    
            _observationServiceMock.Verify(
                x => x.InsertObservation(It.Is<PatientObservation>(o => o.Value.ToString() == "VMNI"), true, false),
                Times.Once);
        }


    /// <summary>
    /// Verifies that when a "Compliancia" (COMPL) observation is mapped, the ventilation mode is calculated and set to "VMI".
    /// </summary>
    [Test]
        public async Task CalculateVentilationMode_Should_Set_VMI_When_COMPL_Arrives()
        {
            var patientId = ObjectId.GenerateNewId();
            var complObservation = new PatientObservation { PatientId = patientId, Name = "Compliancia" };
    
            await _calculatedObservations.Map(complObservation);
    
            _observationServiceMock.Verify(
                x => x.InsertObservation(It.Is<PatientObservation>(o => o.Value.ToString() == "VMI"), true, false),
                Times.Once);
        }

    /// <summary>
    /// Verifies that when an Air Flow observation is processed and a CCC Ventilation Mode observation exists for the patient, the calculated ventilation mode is set to the value retrieved from the CCC source (e.g., "CNAF") and persisted via an insert call.
    /// </summary>
    [Test]
        public async Task CalculateVentilationMode_Should_Set_CCC_Mode_When_AirFlow_Arrives_And_CCC_Has_Mode()
        {
            var patientId = ObjectId.GenerateNewId();
            var airFlowObservation = new PatientObservation { PatientId = patientId, Name = "Air_Flow" };
    
            var mockCursor = new Mock<IAsyncCursor<PatientObservation>>();
            mockCursor.Setup(c => c.Current).Returns(new List<PatientObservation> { new() { Value = "CNAF" } });
            mockCursor.SetupSequence(c => c.MoveNext(It.IsAny<CancellationToken>())).Returns(true).Returns(false);
            mockCursor.SetupSequence(c => c.MoveNextAsync(It.IsAny<CancellationToken>())).ReturnsAsync(true)
                .ReturnsAsync(false);
    
            _observationServiceMock.Setup(x => x.FindByPatientIdAndCodingSystemAsync(patientId, "CCC", "Ventilation_Mode"))
                .ReturnsAsync(mockCursor.Object);
    
    
            await _calculatedObservations.Map(airFlowObservation);
    
            _observationServiceMock.Verify(
                x => x.InsertObservation(It.Is<PatientObservation>(o => o.Value.ToString() == "CNAF"), true, false),
                Times.Once);
        }

    [Test]
    public async Task CalculateVentilationMode_Should_Insert_When_AirFlow_Arrives_And_CCC_Mode()
    {
        var patientId = ObjectId.GenerateNewId();
        var airFlowObservation = new PatientObservation { PatientId = patientId, Name = "Air_Flow" };

        var mockCursor = new Mock<IAsyncCursor<PatientObservation>>();

        // Configura la lista de observaciones devueltas por el cursor
        var observations = new List<PatientObservation> { new() { Value = "CNAF" } };
        mockCursor.Setup(c => c.Current).Returns(observations);

        // Configura el comportamiento de MoveNext y MoveNextAsync para simular la enumeración
        mockCursor.SetupSequence(c => c.MoveNext(It.IsAny<CancellationToken>()))
            .Returns(true) // Primera llamada: hay datos
            .Returns(false); // Segunda llamada: no hay más datos

        mockCursor.SetupSequence(c => c.MoveNextAsync(It.IsAny<CancellationToken>()))
            .ReturnsAsync(true) // Primera llamada asíncrona: hay datos
            .ReturnsAsync(false); // Segunda llamada: no hay más datos

        // Configura el mock del servicio para devolver el cursor simulado
        _observationServiceMock.Setup(x => x.FindByPatientIdAndCodingSystemAsync(patientId, "CCC", "Ventilation_Mode"))
            .ReturnsAsync(mockCursor.Object);


        await _calculatedObservations.Map(airFlowObservation);

        _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
            Times.Once);
    }

    /// <summary>
    /// Verifies that when a PI observation is processed and a COMPL observation already exists for the patient, no new calculated observation is inserted.
    /// </summary>
    [Test]
        public async Task CalculateVentilationMode_Should_Not_Insert_When_PI_Arrives_And_COMPL_Already_Exists()
        {
            var patientId = ObjectId.GenerateNewId();
            var piObservation = new PatientObservation { PatientId = patientId, Name = "PI" };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "COMPL" }))
                .ReturnsAsync([new PatientObservation { Name = "COMPL" }]); // Existe COMPL
    
            await _calculatedObservations.Map(piObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never);
        }

    /// <summary>
    /// Verifies that no calculated observation is inserted when a PS (Pressure Support) observation is processed
    /// and a COMPL observation already exists for the patient.
    /// </summary>
    [Test]
        public async Task CalculateVentilationMode_Should_Not_Insert_When_PS_Arrives_And_COMPL_Already_Exists()
        {
            var patientId = ObjectId.GenerateNewId();
            var psObservation = new PatientObservation { PatientId = patientId, Name = "PS" };
    
            _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List<string> { "COMPL" }))
                .ReturnsAsync([new PatientObservation { Name = "COMPL" }]); // Existe COMPL
    
            await _calculatedObservations.Map(psObservation);
    
            _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never);
        }

    //Las opciones pueden ser VV, VA o ECMO.
    //Cuando llegue VV se muestra VV.
    //Cuando llegue VA se muestra VA.
    //Cuando llegue los siguientes parámetros, se debe mostrar ECMO: ECCO2r, VVDL, VA+V, VVDL+V, VV+V, VVA

    /// <summary>
    /// Verifies that mapping a <see cref="PatientObservation"/> with Name "ECMO_Location" and Value "VV" returns a non-null result whose value is "VV".
    /// </summary>
    [Test]
        public async Task CalculateEcmoLocation_Should_Return_VV_When_Value_Is_VV()
        {
            var observation = new PatientObservation { Name = "ECMO_Location", Value = "VV" };
            var result = await _calculatedObservations.Map(observation);
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Value, Is.EqualTo("VV"));
        }

    /// <summary>
    /// Verifies that the ECMO location calculation returns "VA" when the input observation value is "VA".
    /// </summary>
    [Test]
        public async Task CalculateEcmoLocation_Should_Return_VA_When_Value_Is_VA()
        {
            var observation = new PatientObservation { Name = "ECMO_Location", Value = "VA" };
            var result = await _calculatedObservations.Map(observation);
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Value, Is.EqualTo("VA"));
        }

    /// <summary>
    /// Verifies that the mapping logic returns "ECMO" when the observation value is one of the recognized ECMO location codes
    /// (e.g., "ECCO2r", "VVDL", "VA+V", "VVDL+V", "VV+V", "VVA").
    /// </summary>
    /// <param name="inputValue">The ECMO location code assigned to the <see cref="PatientObservation"/> value under test.</param>
    [Test]
        [TestCase("ECCO2r")]
        [TestCase("VVDL")]
        [TestCase("VA+V")]
        [TestCase("VVDL+V")]
        [TestCase("VV+V")]
        [TestCase("VVA")]
        public async Task CalculateEcmoLocation_Should_Return_ECMO_When_Value_Is_In_Ecmo_List(string inputValue)
        {
            var observation = new PatientObservation { Name = "ECMO_Location", Value = inputValue };
            var result = await _calculatedObservations.Map(observation);
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Value, Is.EqualTo("ECMO"));
        }

    /// <summary>
    /// Verifies that the ECMO Location mapping does not alter the observation value when the provided value is not present in the recognized ECMO list, leaving the original value unchanged.
    /// </summary>
    [Test]
        public async Task CalculateEcmoLocation_Should_Not_Change_Value_When_Not_In_Ecmo_List()
        {
            var observation = new PatientObservation { Name = "ECMO_Location", Value = "OTHER" };
            var result = await _calculatedObservations.Map(observation);
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Value, Is.EqualTo("OTHER"));
        }


    /// <summary>
    /// Verifies that the mapping of a <see cref="PatientObservation"/> through the calculated observations mapper returns the original observation unchanged, ensuring the mapping is a no-op for patient observations.
    /// </summary>
    [Test]
        public async Task CalculateEcmoLocation_Should_Return_Original_Observation_When_Not_PatientObservation()
        {
            var observation = new PatientObservation();
            var result = await _calculatedObservations.Map(observation);
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.EqualTo(observation));
        }

    // Mandar observación 'SOBREANALGESIA'
    // 0 <= EVN <= 3 o ESCID = 3 o ANI > 70
    // y
    // Perfusiones de morfina, remifentanilo 0 fentanilo sostenidas > 24h.

    /// <summary>
    /// Verifies that an "Over_Analgesia" observation with value "True" is created when the patient has active analgesic treatments (Morfina, Remifentanilo) and the last ANI observation indicates a high value (80), suggesting over-analgesia.
    /// </summary>
    [Test]
        public async Task CalculateOverAnalgesia_Should_Create_Observation_When_Conditions_Are_Met()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "EVN", Value = "3", PatientId = patientId };
            var morfina = new PatientTreatment
                { RequestedGiveCodes = [new Code { Text = "Morfina" }], StartTime = DateTime.UtcNow.AddHours(-25) };
            var remifentanilo = new PatientTreatment
                { RequestedGiveCodes = [new Code { Text = "Remifentanilo" }], StartTime = DateTime.UtcNow.AddHours(-25) };
            //var fentanilo = new PatientTreatment
            //    { RequestedGiveCodes = [new Code { Text = "Fentanilo" }], StartTime = DateTime.UtcNow.AddHours(-25) };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
                .ReturnsAsync([new PatientObservation { Name = "ANI", Value = "80" }]);
    
            _treatmentServiceMock
                .Setup(s => s.GetActiveTreatmentsByPatient(patientId))
                .ReturnsAsync(new List<PatientTreatment>
                    { morfina, remifentanilo /*, fentanilo*/ }); // No hay tratamientos de analgesia activos
    
            _observationServiceMock
                .Setup(s => s.InsertObservation(It.IsAny<PatientObservation>(), true, false))
                .Returns(Task.CompletedTask);
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            _observationServiceMock.Verify(
                x => x.InsertObservation(
                    It.Is<PatientObservation>(o =>
                        o.PatientId == patientId && o.Name == "Over_Analgesia" && o.Value.ToString() == "True"),
                    true, false),
                Times.Once
            );
        }

    [Test]
    public async Task CalculateOverAnalgesia_Should_Not_Create_Observation_When_Conditions_Are_Not_Met()
    {
        // Arrange
        var patientId = ObjectId.GenerateNewId();
        var obs = new PatientObservation { Name = "EVN", Value = "5", PatientId = patientId }; // No cumple la condición

        _observationServiceMock
            .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
            .ReturnsAsync([new PatientObservation { Name = "ANI", Value = "60" }]);

        _treatmentServiceMock
            .Setup(s => s.GetActiveTreatmentsByPatient(patientId))
            .ReturnsAsync(new List<PatientTreatment>()); // No hay tratamientos activos

        // Act
        await _calculatedObservations.Map(obs);

        // Assert
        _observationServiceMock.Verify(
            x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
            Times.Never
        );
    }

    //Diferencia entre la Pmeset y la PEEP
    //Calculo Valor = Pmeset - PEEP

    /// <summary>
    /// Verifies that the mapping correctly calculates and inserts the driving pressure (Pmeset minus PEEP) when both source observations are available.
    /// </summary>
    [Test]
        public async Task CalculateDrivingPressure_Should_Calculate_Correctly_When_Both_Observations_Are_Present()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Pmeset", Value = "25", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
                .ReturnsAsync([new PatientObservation { Name = "PEEP", Value = "10" }]);
    
            _observationServiceMock
                .Setup(s => s.InsertObservation(It.IsAny<PatientObservation>(), true, false))
                .Returns(Task.CompletedTask);
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            _observationServiceMock.Verify(
                x => x.InsertObservation(
                    It.Is<PatientObservation>(o =>
                        o.PatientId == patientId && o.Name == "Driving_Pressure" && o.Value.ToString() == "15"),
                    true, false),
                Times.Once
            );
        }

    [Test]
    public async Task CalculateDrivingPressure_Should_Not_Calculate_If_Observation_Is_Missing()
    {
        // Arrange
        var patientId = ObjectId.GenerateNewId();
        var obs = new PatientObservation { Name = "Pmeset", Value = "25", PatientId = patientId };

        _observationServiceMock
            .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
            .ReturnsAsync([]); // No hay observación complementaria

        // Act
        await _calculatedObservations.Map(obs);

        // Assert
        _observationServiceMock.Verify(
            x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
            Times.Never
        );
    }

    // Cálculo dividiendo el ratio SpO2/FiO2 entre la FR
    // Valor = (SpO2_FiO2_Ratio) / FR

    /// <summary>
    /// Verifies that the Rox Index is calculated correctly by dividing the SpO2/FiO2 ratio by the respiratory rate (FR)
    /// when both required observations are available, and that the result is persisted as a new "Rox_Index" observation.
    /// </summary>
    [Test]
        public async Task CalculateRoxIndex_Should_Calculate_Correctly_When_Both_Observations_Are_Present()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "SpO2_FiO2_Ratio", Value = "200", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
                .ReturnsAsync([new PatientObservation { Name = "FR", Value = "25" }]);
    
            _observationServiceMock
                .Setup(s => s.InsertObservation(It.IsAny<PatientObservation>(), true, false))
                .Returns(Task.CompletedTask);
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            _observationServiceMock.Verify(
                x => x.InsertObservation(
                    It.Is<PatientObservation>(o =>
                        o.PatientId == patientId && o.Name == "Rox_Index" && o.Value.ToString() == (200 / 25).ToString()),
                    true, false),
                Times.Once
            );
        }

    [Test]
    public async Task CalculateRoxIndex_Should_Not_Calculate_If_Observation_Is_Missing()
    {
        // Arrange
        var patientId = ObjectId.GenerateNewId();
        var obs = new PatientObservation { Name = "SpO2_FiO2_Ratio", Value = "200", PatientId = patientId };

        _observationServiceMock
            .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
            .ReturnsAsync([]); // No hay observación complementaria

        // Act
        await _calculatedObservations.Map(obs);

        // Assert
        _observationServiceMock.Verify(
            x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
            Times.Never
        );
    }

    /// <summary>
    /// Verifies that the ROX index calculation is not performed when the respiratory rate (FR) is zero,
    /// preventing a division by zero error.
    /// </summary>
    [Test]
        public async Task CalculateRoxIndex_Should_Not_Calculate_When_FR_Is_Zero_To_Avoid_Division_By_Zero()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "SpO2_FiO2_Ratio", Value = "200", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
                .ReturnsAsync([new PatientObservation { Name = "FR", Value = "0" }]);
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            _observationServiceMock.Verify(
                x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never
            );
        }

    // Calculado como el sumatorio de la Diuresis de las últimas 6h 
    // entre el último peso medido del paciente y entre 6. Medido en ml/kg/h
    // Valor = sum(Diuresis últimas 6 horas) / Weight / 6

    /// <summary>
    /// Verifies that the diuresis calculation produces the correct value when prior diuresis observations and a weight observation are available for the patient, resulting in a new "Calculated_Diuresis" observation being inserted.
    /// </summary>
    [Test]
        public async Task CalculateDiuresis_Should_Calculate_Correctly_When_Observations_Are_Available()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Diuresis", Value = "100", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 2, It.IsAny<List<string>>()))
                .ReturnsAsync([
                    new PatientObservation { Name = "Diuresis", Value = "120" },
                    new PatientObservation { Name = "Diuresis", Value = "110" }
                ]);
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
                .ReturnsAsync([new PatientObservation { Name = "Weight", Value = "70" }]);
    
            _observationServiceMock
                .Setup(s => s.InsertObservation(It.IsAny<PatientObservation>(), true, false))
                .Returns(Task.CompletedTask);
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            const double expectedValue = (100 + 120 + 110) / 70.0 / 6;
            _observationServiceMock.Verify(
                x => x.InsertObservation(
                    It.Is<PatientObservation>(o =>
                        o.PatientId == patientId && o.Name == "Calculated_Diuresis" &&
                        Math.Abs((double)o.Value - expectedValue) < 0.1),
                    true, false),
                Times.Once
            );
        }

    /// <summary>
    /// Verifies that diuresis is not calculated when no diuresis observations are found for the patient.
    /// Ensures the mapping logic skips persistence of a calculated diuresis observation when required prior observations are missing.
    /// </summary>
    [Test]
        public async Task CalculateDiuresis_Should_Not_Calculate_If_Diuresis_Observations_Are_Missing()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Diuresis", Value = "100", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 2, It.IsAny<List<string>>()))
                .ReturnsAsync([]); // No hay diuresis
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            _observationServiceMock.Verify(
                x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never
            );
        }

    /// <summary>
    /// Verifies that the diuresis calculation is not performed when the patient has no weight observation available, ensuring no new observation is inserted.
    /// </summary>
    [Test]
        public async Task CalculateDiuresis_Should_Not_Calculate_If_Weight_Is_Invalid()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Diuresis", Value = "100", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 2, It.IsAny<List<string>>()))
                .ReturnsAsync([
                    new PatientObservation { Name = "Diuresis", Value = "120" },
                    new PatientObservation { Name = "Diuresis", Value = "110" }
                ]);
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny<List<string>>()))
                .ReturnsAsync([]); // No hay peso
    
            // Act
            await _calculatedObservations.Map(obs);
    
            // Assert
            _observationServiceMock.Verify(
                x => x.InsertObservation(It.IsAny<PatientObservation>(), true, false),
                Times.Never
            );
        }

    /// <summary>
    /// Verifies that the rehabilitation observation mapping sets the status to Alert when the recent rehabilitation values meet the alert condition.
    /// </summary>
    [Test]
        public async Task CalculateRehabilitationAlarm_Should_Set_Alert_When_Condition_Is_Met()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Rehabilitation", Value = "2", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 6, It.IsAny<List<string>>()))
                .ReturnsAsync([
                    new PatientObservation { Name = "Rehabilitation", Value = "0" },
                    new PatientObservation { Name = "Rehabilitation", Value = "1" },
                    new PatientObservation { Name = "Rehabilitation", Value = "2" },
                    new PatientObservation { Name = "Rehabilitation", Value = "0" },
                    new PatientObservation { Name = "Rehabilitation", Value = "1" },
                    new PatientObservation { Name = "Rehabilitation", Value = "2" }
                ]);
    
            // Act
            var result = await _calculatedObservations.Map(obs);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Status, Is.EqualTo(StatusEnum.Type.Alert));
        }

    /// <summary>
    /// Verifies that the rehabilitation alarm calculation does not set the alert status when the
    /// configured condition is not met, even when the most recent observation values are within an
    /// expected range and an out-of-range value is present among the evaluated history.
    /// </summary>
    [Test]
        public async Task CalculateRehabilitationAlarm_Should_Not_Set_Alert_When_Condition_Is_Not_Met()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Rehabilitation", Value = "2", PatientId = patientId };
    
            _observationServiceMock
                .Setup(s => s.FindLastObservations(patientId, 6, It.IsAny<List<string>>()))
                .ReturnsAsync([
                    new PatientObservation { Name = "Rehabilitation", Value = "0" },
                    new PatientObservation { Name = "Rehabilitation", Value = "1" },
                    new PatientObservation { Name = "Rehabilitation", Value = "2" },
                    new PatientObservation { Name = "Rehabilitation", Value = "1" },
                    new PatientObservation { Name = "Rehabilitation", Value = "3" }, //<--fuera de rango
                    new PatientObservation { Name = "Rehabilitation", Value = "0" }
                ]);
    
            // Act
            var result = await _calculatedObservations.Map(obs);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Status, Is.Not.EqualTo(StatusEnum.Type.Alert));
        }

    /// <summary>
    /// Verifies that the <c>Map</c> method does not set an alert for a Rehabilitation observation when its value is greater than 2, returning the original observation unchanged.
    /// </summary>
    [Test]
        public async Task CalculateRehabilitationAlarm_Should_Not_Set_Alert_When_Value_Is_Greater_Than_2()
        {
            // Arrange
            var patientId = ObjectId.GenerateNewId();
            var obs = new PatientObservation { Name = "Rehabilitation", Value = "3", PatientId = patientId };
    
            // Act
            var result = await _calculatedObservations.Map(obs);
    
            // Assert
            Assert.That(result, Is.SameAs(obs));
        }

    /// <summary>
    /// Verifies that the rehabilitation alarm calculation returns the same observation unchanged when the input observation is not a <see cref="PatientObservation"/>.
    /// This ensures that the mapping method does not perform any transformation for non-patient observations.
    /// </summary>
    [Test]
        public async Task CalculateRehabilitationAlarm_Should_Return_Same_Observation_If_Not_PatientObservation()
        {
            // Arrange
            var obs = new BasePatientObservation(); // No es un PatientObservation
    
            // Act
            var result = await _calculatedObservations.Map(obs);
    
            // Assert
            Assert.That(result, Is.SameAs(obs));
        }
}