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.Models;
using adas_core.Domain.Models.MongoModels;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using Moq;

namespace adas_core.Test.Services;

[TestFixture]
public class RecordingAlertServiceTest
{
    /// <summary>
    /// Initializes the mocked dependencies and test infrastructure required by the <see cref="RecordingAlertService"/> unit tests, including service and repository mocks, a fake HTTP context with a "TestUser" claim, and the system-under-test instance.
    /// </summary>
    [SetUp]
        public void Setup()
        {
            _patientServiceMock = new Mock<IPatientService>();
            _patientServiceLazy = new Lazy<IPatientService>(() => _patientServiceMock.Object);
            _configObservationServiceMock = new Mock<IConfigObservationService>();
            _recordingAlertRepositoryMock = new Mock<IRecordingAlertRepository>();
            _recordingAlertArchiveRepositoryMock = new Mock<IRecordingAlertArchiveRepository>();
            _clientMessageServiceMock = new Mock<IClientMessageService>();
            _subscribersServiceMock = new Mock<ISubscribersService>();
            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);
    
            //optionsApiSettings = Microsoft.Extensions.Options.Options.Create<ApiSettings>(apiSettings);            
    
            _logger = new Mock<ILogger<RecordingAlertService>>();
    
            _recordingAlertService = new RecordingAlertService(
                _patientServiceLazy,
                _configObservationServiceMock.Object,
                _recordingAlertRepositoryMock.Object,
                _recordingAlertArchiveRepositoryMock.Object,
                //optionsApiSettings,                
                _logger.Object,
                _clientMessageServiceMock.Object,
                _subscribersServiceMock.Object,
                _httpContextAccessorMock.Object,
                _auditServiceMock.Object
            );
        }

    private RecordingAlertService _recordingAlertService = null!;
    private Mock<IPatientService> _patientServiceMock = null!;
    private Lazy<IPatientService> _patientServiceLazy = null!;
    private Mock<IConfigObservationService> _configObservationServiceMock = null!;
    private Mock<IRecordingAlertRepository> _recordingAlertRepositoryMock = null!;
    private Mock<IRecordingAlertArchiveRepository> _recordingAlertArchiveRepositoryMock = null!;
    private Mock<IClientMessageService> _clientMessageServiceMock = null!;
    private Mock<ISubscribersService> _subscribersServiceMock = null!;
    private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock = new();

    private readonly Mock<ILocalAuditService> _auditServiceMock = new();
    //private readonly ApiSettings apiSettings = new ()
    //{

    //};
    //IOptions<ApiSettings> optionsApiSettings;


    private Mock<ILogger<RecordingAlertService>> _logger = null!;

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


    /// <summary>
    /// Verifies that <see cref="RecordingAlertService.SaveRequest"/> does not persist a <see cref="PatientRecordingAlert"/> when the provided <see cref="ApiRequest"/> is neither a recording alert nor an ORU R01 message.
    /// </summary>
    [Test]
        public async Task SaveRequest_Not_RecordingAlert_Not_ORU_R01_Return_not_insert()
        {
            var apiRequest = new ApiRequest();
    
            await _recordingAlertService.SaveRequest(apiRequest);
    
            _recordingAlertRepositoryMock.Verify(d => d.InsertOneAsync(It.IsAny<PatientRecordingAlert>()), Times.Never);
        }

    /// <summary>
    /// Verifies that <see cref="RecordingAlertService.SaveRequest"/> does not insert a <see cref="PatientRecordingAlert"/> when both the patient number and point of care are null in the <see cref="ApiRequest"/>, even when the message type is "ORU_R11".
    /// </summary>
    [Test]
        public async Task SaveRequest_patientNumber_and_pointOfCare_null_Return_not_insert()
        {
            var apiRequest = new ApiRequest
            {
                Type = "ORU_R11",
                MessageTime = Now
            };
    
            await _recordingAlertService.SaveRequest(apiRequest);
    
            _recordingAlertRepositoryMock.Verify(d => d.InsertOneAsync(It.IsAny<PatientRecordingAlert>()), Times.Never);
        }

    /// <summary>
    /// Verifies that the SaveRequest method does not insert a patient recording alert when the specified patient cannot be found.
    /// </summary>
    [Test]
        public async Task SaveRequest_Not_Find_Patient_Return_not_insert()
        {
            var person = new Person
            {
                FirstName = "Miguel",
                LastName = "Villanueva",
                Ids = new Dictionary<string, string> { { "MR", "437537" } }
            };
    
            var apiRequest = new ApiRequest
            {
                Location = new PatientLocation("UCI5C", "Box4"),
                Patient = person,
                PatientNumber = "437537",
                Type = "ORU_R01",
                PatientId = PatientId.ToString(),
                MessageTime = Now
            };
    
            await _recordingAlertService.SaveRequest(apiRequest);
    
            _recordingAlertRepositoryMock.Verify(d => d.InsertOneAsync(It.IsAny<PatientRecordingAlert>()), Times.Never);
        }

    /// <summary>
    /// Verifies that SaveRequest inserts a new <see cref="PatientRecordingAlert"/> into the repository when the configuration observation service returns no retention actions for the given recording alert.
    /// </summary>
    [Test]
        public async Task SaveRequest_Alert_Return_insert()
        {
            var person = new Person
            {
                FirstName = "Miguel",
                LastName = "Villanueva",
                Ids = new Dictionary<string, string> { { "MR", "437537" } }
            };
    
            var patient = new Patient
            {
                Id = ObjectId.GenerateNewId(),
                UnitString = "UCI5C",
                Bed = "Box4",
                PatientNumber = "437537",
                Person = person
            };
    
            var recordingAlert = new PatientRecordingAlert
            {
                IsRecording = true
            };
    
            var apiRequest = new ApiRequest
            {
                Location = new PatientLocation("UCI5C", "Box4"),
                Patient = person,
                PatientNumber = "437537",
                Type = "ORU_R01",
                PatientId = PatientId.ToString(),
                MessageTime = Now,
                RecordingAlert = recordingAlert
            };
    
            _patientServiceMock.Setup(p => p.FindPatientByApiRequest(apiRequest)).ReturnsAsync(patient);
            _configObservationServiceMock.Setup(c => c.RetentionActions(It.IsAny<PatientRecordingAlert>()))
                .ReturnsAsync((ObservatitonRetentionResult?)null);
    
            await _recordingAlertService.SaveRequest(apiRequest);
    
            _recordingAlertRepositoryMock.Verify(d => d.InsertOneAsync(It.IsAny<PatientRecordingAlert>()), Times.Once);
        }
}