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

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

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

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

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

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

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

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


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


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

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

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

    [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

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

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

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

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


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

    [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

    [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

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

    [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

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

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

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

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

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

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

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