using adas_core.Application.Exceptions;
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.MongoModels;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using Moq;
using static NUnit.Framework.Assert;

namespace adas_core.Test.Services;

[TestFixture]
public class TreatmentServiceTest
{
    /// <summary>
    /// Initializes the mock dependencies and creates a <see cref="TreatmentService"/> instance for unit testing.
    /// </summary>
    [SetUp]
    public void Setup()
    {
        _treatmentRepositoryMock = new Mock<ITreatmentRepository>();
        _treatmentArchiveRepositoryMock = new Mock<ITreatmentArchiveRepository>();
        //pocMappingServiceMock = new Mock<IPoCMappingService>();
        _patientServiceMock = new Mock<IPatientService>();
        //calculatedObservationsServiceMock = new Mock<ICalculatedObservationsService>();
        _configObservationServiceMock = new Mock<IConfigObservationService>();
        _clientMessageServiceMock = new Mock<IClientMessageService>();
        _subscribersServiceMock = new Mock<ISubscribersService>();
        _calculatedObservationsServiceMock = new Mock<ICalculatedObservationsService>();
        _unitServiceMock = new Mock<IUnitService>();
        _calculatedObservationsServiceLazy =
            new Lazy<ICalculatedObservationsService>(() => _calculatedObservationsServiceMock.Object);

        //optionsApiSettings = Microsoft.Extensions.Options.Options.Create<ApiSettings>(apiSettings);

        _logger = new Mock<ILogger<TreatmentService>>();


        _treatmentService = new TreatmentService(
            _treatmentRepositoryMock.Object,
            _treatmentArchiveRepositoryMock.Object,
            _patientServiceMock.Object,
            _configObservationServiceMock.Object,
            //optionsApiSettings,
            _logger.Object,
            _clientMessageServiceMock.Object,
            _subscribersServiceMock.Object,
            _calculatedObservationsServiceLazy,
            _unitServiceMock.Object,
            _httpContextAccessor.Object,
            _auditService.Object
        );
    }

    private TreatmentService _treatmentService = null!;

    private Mock<ITreatmentRepository> _treatmentRepositoryMock = null!;

    private Mock<ITreatmentArchiveRepository> _treatmentArchiveRepositoryMock = null!;

    //Mock<IPoCMappingService> pocMappingServiceMock = null!;
    private Mock<IPatientService> _patientServiceMock = null!;

    //Mock<ICalculatedObservationsService> calculatedObservationsServiceMock = null!;
    private Mock<IConfigObservationService> _configObservationServiceMock = null!;

    private Mock<IClientMessageService> _clientMessageServiceMock = null!;
    private Mock<ISubscribersService> _subscribersServiceMock = null!;
    private Lazy<ICalculatedObservationsService> _calculatedObservationsServiceLazy = null!;
    private Mock<ICalculatedObservationsService> _calculatedObservationsServiceMock = null!;
    private Mock<IUnitService> _unitServiceMock = null!;
    private readonly Mock<IHttpContextAccessor> _httpContextAccessor = new();
    private readonly Mock<ILocalAuditService> _auditService = new();

    //readonly ApiSettings apiSettings = new ()
    //{
    //    ConfigObservation = new ConfigObservationSettings
    //    {
    //        IgnoreUnknownObservation = false
    //    }
    //};
    //IOptions<ApiSettings> optionsApiSettings;


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

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

    //3 Nuevos tratamiento3,5,6
    private readonly List<PatientTreatment> _patientTreatments =
    [
        new()
        {
            Id = ObjectId.GenerateNewId(),
            PatientId = PatientId,
            OrderControl = OrderControlType.Nw,
            PlacerOrder = new Entity { EntityIdentifier = "3690", NamespaceId = "CareVue" },
            FillerOrder = new Entity { EntityIdentifier = "3690", NamespaceId = "CareVue" },
            OrderStatus = "A",
            OrderTime = DateTime.Now,
            Notes = [],
            Routes = []
        },

        new()
        {
            Id = ObjectId.GenerateNewId(),
            PatientId = PatientId,
            OrderControl = OrderControlType.Dc,
            PlacerOrder = new Entity { EntityIdentifier = "3690", NamespaceId = "CareVue" },
            FillerOrder = new Entity { EntityIdentifier = "3690", NamespaceId = "CareVue" },
            OrderStatus = "A",
            OrderTime = DateTime.Now,
            Notes = [],
            Routes = []
        },

        new()
        {
            Id = ObjectId.GenerateNewId(),
            PatientId = PatientId,
            OrderControl = OrderControlType.Nw,
            PlacerOrder = new Entity { EntityIdentifier = "4590", NamespaceId = "CareVue" },
            FillerOrder = new Entity { EntityIdentifier = "4590", NamespaceId = "CareVue" },
            OrderStatus = "A",
            OrderTime = DateTime.Now,
            Notes = [],
            Routes = []
        },

        new()
        {
            Id = ObjectId.GenerateNewId(),
            PatientId = PatientId,
            OrderControl = OrderControlType.Xo,
            PlacerOrder = new Entity { EntityIdentifier = "4590", NamespaceId = "CareVue" },
            FillerOrder = new Entity { EntityIdentifier = "4590", NamespaceId = "CareVue" },
            OrderStatus = "A",
            OrderTime = DateTime.Now,
            Notes = [],
            Routes = []
        },


        new()
        {
            Id = ObjectId.GenerateNewId(),
            PatientId = PatientId,
            OrderControl = OrderControlType.Nw,
            PlacerOrder = new Entity { EntityIdentifier = "2100", NamespaceId = "CareVue" },
            FillerOrder = new Entity { EntityIdentifier = "2100", NamespaceId = "CareVue" },
            OrderStatus = "A",
            OrderTime = DateTime.Now,
            Notes = [],
            Routes = []
        },


        new()
        {
            Id = ObjectId.GenerateNewId(),
            PatientId = PatientId,
            OrderControl = OrderControlType.Nw,
            PlacerOrder = new Entity { EntityIdentifier = "100", NamespaceId = "CareVue" },
            FillerOrder = new Entity { EntityIdentifier = "100", NamespaceId = "CareVue" },
            OrderStatus = "A",
            OrderTime = DateTime.Now,
            Notes = [],
            Routes = []
        }
    ];

    /// <summary>
    /// Verifies that saving an <see cref="ApiRequest"/> without the Case option throws an <see cref="ApiRequestException"/>, preventing the insert operation.
    /// </summary>
    /// <exception cref="ApiRequestException">Thrown by the service when the Case option is not provided in the request.</exception>
    [Test]
    public void SaveRequest_Not_Case_option_Return_not_insert()
    {
        var apiRequest = new ApiRequest();

        Func<Task> act = async () => await _treatmentService.SaveRequest(apiRequest);
        Assert.ThrowsAsync<ApiRequestException>(act);
    }

    /// <summary>
    /// Verifies that _treatmentService.SaveRequest throws an <see cref="ApiRequestException"/> when the <see cref="ApiRequest"/> is missing both the patient number and the point of care, preventing insertion of an incomplete request.
    /// </summary>
    [Test]
    public void SaveRequest_patientNumber_and_pointOfCare_null_Return_not_insert()
    {
        var apiRequest = new ApiRequest
        {
            Type = "ORU_R01",
            MessageTime = Now
        };

        Func<Task> act = async () => await _treatmentService.SaveRequest(apiRequest);
        Assert.ThrowsAsync<ApiRequestException>(act);
    }

    /// <summary>
    /// Verifies that _treatmentService.SaveRequest does not insert a patient treatment record when the patient cannot be found in the archive.
    /// </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 = "OMP_O09",
            PatientId = PatientId.ToString(),
            MessageTime = Now
        };

        await _treatmentService.SaveRequest(apiRequest);

        _treatmentArchiveRepositoryMock.Verify(d => d.InsertOneAsync(It.IsAny<PatientTreatment>()), Times.Never);
    }

    /// <summary>
    /// Verifies that SaveRequest does not insert a new patient treatment into the treatment archive when an existing patient is found by patient number.
    /// </summary>
    [Test]
    public async Task SaveRequest_Find_Patient_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 apiRequest = new ApiRequest
        {
            Location = new PatientLocation("UCI5C", "Box4"),
            Patient = person,
            PatientNumber = "437537",
            Type = "OMP_O09",
            PatientId = PatientId.ToString(),
            MessageTime = Now
        };

        _patientServiceMock.Setup(p => p.FindByPatientNumber(apiRequest.PatientNumber, false)).ReturnsAsync(patient);

        await _treatmentService.SaveRequest(apiRequest);

        _treatmentArchiveRepositoryMock.Verify(d => d.InsertOneAsync(It.IsAny<PatientTreatment>()), Times.Never);
    }

    /// <summary>
    /// Verifies that <c>GetActiveTreatmentsByPatient</c> retrieves the full set of active treatments for the specified patient, returning a non-empty collection that includes all expected treatment records identified by their placer order entity identifiers.
    /// </summary>
    [Test]
    public async Task GetActiveTreatmentsByPatient()
    {
        _treatmentRepositoryMock.Setup(t => t.GetByPatientId(PatientId))
            .ReturnsAsync(_patientTreatments.AsEnumerable());

        var treatments = await _treatmentService.GetActiveTreatmentsByPatient(PatientId);
        var result = treatments.ToList();

        using (Assert.EnterMultipleScope())
        {
            That(result, Is.Not.Empty);
            That(result, Has.Count.EqualTo(4));
            That(result.Find(t => t?.PlacerOrder?.EntityIdentifier == "100"), Is.Not.Null);
            That(result.Find(t => t?.PlacerOrder?.EntityIdentifier == "2100"), Is.Not.Null);
            That(result.Find(t => t?.PlacerOrder?.EntityIdentifier == "4590"), Is.Not.Null);
            That(result.Find(t => t?.PlacerOrder?.EntityIdentifier == "3690"), Is.Not.Null);
        }
        ;
    }
}