using System.Security.Claims;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using Moq;
using Options = Microsoft.Extensions.Options.Options;

namespace adas_core.Test.Services;

/// <summary>
/// Contains unit tests for verifying the behavior and functionality of the <see cref="AlarmService"/> class.
/// </summary>
public class AlarmServiceTest
{
    private readonly Mock<IAlarmRepository> _alarmRepositoryMock;
    private readonly AlarmService _alarmService;


    private readonly ApiSettings _apiSettings = new()
    {
        ConfigObservation = new ConfigObservationSettings
        {
            IgnoreUnknownObservation = false
        },
        IntravenousLinesCode = ["10546003"],
        AllergiesCode = ["473011001"],
        DrainageCode = ["56868008"],
        IsolationCode = ["302147001"],
        PositionCode = ["386053000"]
    };

    private readonly Mock<ICalculatedObservationsService> _calculatedObservationsServiceMock;
    private readonly Mock<IConfigObservationService> _configObservationServiceMock;
    private readonly Mock<IObservationService> _observationServiceMock;
    private readonly Mock<IPatientService> _patientServiceMock;

    private readonly RecordingSettings _recordingSettings = new();
    private readonly Mock<IUnitService> _unitServiceMock;

    public AlarmServiceTest()
    {
        var optionsApiSettings = Options.Create(_apiSettings);
        Options.Create(_recordingSettings);

        _observationServiceMock = new Mock<IObservationService>();
        var observationServiceMockLazy = new Lazy<IObservationService>(() => _observationServiceMock.Object);

        _patientServiceMock = new Mock<IPatientService>();
        _configObservationServiceMock = new Mock<IConfigObservationService>();
        _unitServiceMock = new Mock<IUnitService>();
        var pocServiceMock = new Mock<IPointOfCareService>();

        var beaconServiceMock = new Mock<ILightBeaconService>();
        var beaconServiceMockLazy = new Lazy<ILightBeaconService>(() => beaconServiceMock.Object);

        var relayServiceMock = new Mock<IRelayService>();
        var relayServiceMockLazy = new Lazy<IRelayService>(() => relayServiceMock.Object);

        var recordingServiceMock = new Mock<IRecordingService>();
        var recordingServiceMockLazy = new Lazy<IRecordingService>(() => recordingServiceMock.Object);

        var clientMessageServiceMock = new Mock<IClientMessageService>();

        var subscribersServiceMock = new Mock<ISubscribersService>();

        var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
        var auditServiceMock = new Mock<ILocalAuditService>();

        _calculatedObservationsServiceMock = new Mock<ICalculatedObservationsService>();
        var calculatedObservationsServiceLazy =
            new Lazy<ICalculatedObservationsService>(() => _calculatedObservationsServiceMock.Object);


        var logger = new Mock<ILogger<AlarmService>>();

        _alarmRepositoryMock = new Mock<IAlarmRepository>();
        var userClaims = new ClaimsPrincipal(new ClaimsIdentity([
            new Claim(ClaimTypes.Name, "TestUser")
        ], "mock"));

        var httpContextMock = new DefaultHttpContext
        {
            User = userClaims
        };

        httpContextAccessorMock.Setup(accessor => accessor.HttpContext)
            .Returns(httpContextMock);
        _alarmService = new AlarmService(
            _alarmRepositoryMock.Object,
            logger.Object,
            _patientServiceMock.Object,
            _configObservationServiceMock.Object,
            observationServiceMockLazy,
            clientMessageServiceMock.Object,
            subscribersServiceMock.Object,
            calculatedObservationsServiceLazy,
            beaconServiceMockLazy,
            recordingServiceMockLazy,
            relayServiceMockLazy,
            optionsApiSettings,
            _unitServiceMock.Object,
            pocServiceMock.Object,
            httpContextAccessorMock.Object,
            auditServiceMock.Object,
            false // Disable the timer for testing
        );
    }


    /// <summary>
    /// Verifies that when an ORU_R40 type request is processed without any alarms, the patient is located but the observation is not persisted.
    /// </summary>
    [Test]
        public async Task SaveRequest_Oru_R40_Type_Without_Alarms_Dont_Insert()
        {
            // Arrange
            var apiRequest = new ApiRequest
            {
                Type = "ORU_R40",
                PatientNumber = "12345",
                Observation = new PatientObservation { Value = "Test" },
                Alarms = [],
                Location = new PatientLocation { Bed = "Bed1", Room = "Bed1", UnitName = "Unit1" }
            };
            var unit = new Unit { Configuration = new UnitConfiguration { AutoAdt = true } };
            var patient = new Patient { Id = ObjectId.GenerateNewId(), Bed = "Bed1" };
            _unitServiceMock.Setup(u => u.FindByUnitNameOrPocName(It.IsAny<string>(), It.IsAny<string>()))
                .ReturnsAsync(unit);
            _patientServiceMock.Setup(p => p.FindPatientByApiRequest(apiRequest)).ReturnsAsync(patient);
            _observationServiceMock.Setup(o => o.SaveRequestAsync(It.IsAny<ApiRequest>())).Returns(Task.CompletedTask);
    
            // Act
            await _alarmService.SaveRequest(apiRequest);
    
            // Assert
            _patientServiceMock.Verify(p => p.FindPatientByApiRequest(apiRequest), Times.Once);
            _observationServiceMock.Verify(o => o.SaveRequestAsync(It.IsAny<ApiRequest>()), Times.Never);
        }

    /// <summary>
    /// Verifies that <see cref="AlarmService.ProcessAlarmObservations"/> inserts the provided alarm observations into the alarm repository.
    /// </summary>
    [Test]
        public async Task ProcessAlarmObservations_Should_Insert_Alarm_Observations()
        {
            // Arrange
            var patient = new Patient { Id = ObjectId.GenerateNewId(), Bed = "Bed1" };
            var alarmObservations = new List<PatientObservationAlarm>
            {
                new() { Value = "Alarm1", Time = DateTime.UtcNow}
            };
            var observations = new List<PatientObservation>
            {
                new() { Value = "Alarm1", Time = DateTime.MinValue}
            };
    
            _configObservationServiceMock.Setup(o => o.Map(It.IsAny<PatientObservationAlarm>(), It.IsAny<bool>()))
                .ReturnsAsync((PatientObservationAlarm obs, bool _) => obs); // Correct setup
    
            _calculatedObservationsServiceMock.Setup(o => o.Map(It.IsAny<PatientObservationAlarm>(), It.IsAny<bool>()))
                .ReturnsAsync((PatientObservationAlarm obs, bool _) => obs); // Correct setup
    
            // Act
            await _alarmService.ProcessAlarmObservations(alarmObservations, observations, patient, DateTime.UtcNow);
    
            // Assert
            _alarmRepositoryMock.Verify(a => a.InsertOneAsync(It.IsAny<PatientObservationAlarm>()), Times.Once);
        }

    /// <summary>
    /// Verifies that when checking observation alarms for a patient observation with an "Event_PEEP_Low" configuration
    /// (coding system "ADAS_EVENT", description "EVT_LO and EVT_EXTR_LO"), the alarm service does not insert a new observation,
    /// as indicated by the verification that <c>InsertObservation</c> is never invoked.
    /// </summary>
    [Test]
        public async Task CheckObservationAlarm_Alarm_EventPEEP_PEEP_bajo_create_Event()
        {
            var obs = new PatientObservation();
    
            var configs = new ConfigObservation
            {
                Id = ObjectId.GenerateNewId(),
                Name = "Event_PEEP_Low",
                CodingSystem = "ADAS_EVENT",
                Description = "EVT_LO and EVT_EXTR_LO"
            };
    
            _configObservationServiceMock.Setup(c => c.Get(It.IsAny<PatientObservation>(), false))
                .ReturnsAsync(configs);
    
            await _alarmService.CheckObservationAlarm(obs);
    
    
            _observationServiceMock.Verify(o => o.InsertObservation(obs, true, true), Times.Never);
        }

    /// <summary>
    /// Verifies that <see cref="AlarmService.CheckObservationAlarm"/> processes a patient observation
    /// matching a configured rule (name "EVT_LO" with required value "PEEP") by inserting a new
    /// observation with the original value preserved (e.g., "PEEP High") while the alarm
    /// configuration handles the red beacon signaling.
    /// </summary>
    [Test]
        public async Task CheckObservationAlarm_Should_Launch_Red_Alarm_And_Set_BeaconColor_Red()
        {
            var alarmConfig = new AlarmConfig
            {
                Enabled = true,
                Priority = 10,
                Beacon = new AlarmItem
                {
                    Enabled = true,
                    BeaconColor = AlarmEnum.BeaconColor.Red, // Using the enum value
                    EndAfter = 60 // Assuming the alarm should end after 60 seconds
                }
            };
    
            var configs = new ConfigObservation
            {
                Id = ObjectId.GenerateNewId(),
                Name = "Event_PEEP_Low",
                CodingSystem = "ADAS_EVENT",
                Description = "logs an event. No generate an alarm.",
                Alarm = alarmConfig,
                RequiredValue = "PEEP"
            };
    
            var obsConfig = new ConfigObservation
            {
                Id = ObjectId.GenerateNewId(),
                Name = "EVT_LO",
                CheckObservations = true,
                CreateObservation = [configs]
            };
            var patId = ObjectId.GenerateNewId();
            // Arrange
            var obs = new PatientObservation
            {
                Id = ObjectId.GenerateNewId(),
                Name = "EVT_LO",
                PatientId = patId,
                Value = "PEEP High",
                Time = DateTime.UtcNow,
                CheckObservations = true,
                CreateObservation = [configs]
            };
    
            _configObservationServiceMock.Setup(c => c.Get(It.IsAny<PatientObservation>(), false))
                .ReturnsAsync(obsConfig);
    
    
            // Act
            await _alarmService.CheckObservationAlarm(obs);
    
            _observationServiceMock.Verify(
                o => o.InsertObservation(
                    It.Is<PatientObservation>(ob => ob.Value.ToString() == "PEEP High"), true, true),
                Times.Once);
        }
}