using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Infrastructure.Repositories;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using Options = Microsoft.Extensions.Options.Options;

namespace adas_core.Test.Repositories;

[TestFixture]
[Category("Integration")]
public class AppointmentRepositoryTest
{
    /// <summary>
    /// Performs one-time setup for integration tests by resetting the patients_appointments collection and seeding it with two test patient appointments: one with minimal fields and another extended with visit number, appointment reason, and resource groups.
    /// </summary>
    [OneTimeSetUp]
        public async Task Init()
        {
            _optionsApiSettings = Options.Create(_apiSettings);
    
            var patient = new Person
            {
                FirstName = "firstName",
                SecondName = "secondName",
                BirthDate = Now.AddYears(-10),
                Gender = PatientEnum.Gender.Female,
                Ids = new Dictionary<string, string> { { "MR", "123456" }, { "VN", "654321" } }
            };
    
            var testPatientAppointment = new PatientAppointment
            {
                PatientId = PatientId,
                Patient = patient,
                CreateTime = Now.AddHours(-2),
                UpdateTime = Now.AddHours(-2)
            };
    
            var testPatientAppointment2 = new PatientAppointment
            {
                PatientId = PatientId,
                Patient = patient,
                CreateTime = Now.AddHours(-3),
                UpdateTime = Now.AddHours(-3),
                VisitNumber = "654321",
                AppointmentReason = "appointmentReason",
                ResourceGroups =
                [
                    new PatientAppointmentResourceGroup
                    {
                        Locations = [new PatientLocation("UCI5C", "Box4")]
                    }
                ]
            };
    
            await IntegrationDb.Database.DropCollectionAsync("patients_appointments");
            await IntegrationDb.Database.CreateCollectionAsync("patients_appointments");
    
            _repository = new AppointmentRepository(_optionsApiSettings, IntegrationDb.Database);
    
            await _repository.InsertOneAsync(testPatientAppointment);
            await _repository.InsertOneAsync(testPatientAppointment2);
        }

    private AppointmentRepository _repository;

    private readonly ApiSettings _apiSettings = new()
    {
        PatientsAppointments = "patients_appointments"
    };

    private IOptions<ApiSettings> _optionsApiSettings;

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

    /// <summary>
    /// Verifies that <see cref="AppointmentRepository"/>.GetByPatient returns a non-null, non-empty list when a valid patient identifier is provided.
    /// </summary>
    [Test]
        public async Task GetByPatient_Find_Patient_Return_List()
        {
            var result = await _repository.GetByPatient(PatientId);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Not.Empty);
        }

    /// <summary>
    /// Verifies that <see cref="AppointmentRepository.GetByPatient"/> returns a non-null, empty list when no records are found for the specified patient identifier.
    /// </summary>
    [Test]
        public async Task GetByPatient_not_Find_Patient_Return_Empty_List()
        {
            var result = await _repository.GetByPatient(ObjectId.GenerateNewId());
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Empty);
        }

    /// <summary>
    /// Verifies that the repository returns <c>null</c> when no patient is found matching the specified patient identifier and visit number.
    /// </summary>
    [Test]
        public async Task FindByPatientAndVisitNumber_Not_Find_Patient_Return_Null()
        {
            var result = await _repository.FindByPatientAndVisitNumber(PatientId, "visitNumber");
    
            Assert.That(result, Is.Null);
        }

    /// <summary>
    /// Verifies that the repository's FindByPatientAndVisitNumber method successfully retrieves
    /// the corresponding appointment when a valid patient identifier and an existing visit number are supplied.
    /// </summary>
    [Test]
        public async Task FindByPatientAndVisitNumber_Find_Patient_Return_Appointment()
        {
            var result = await _repository.FindByPatientAndVisitNumber(PatientId, "654321");
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result!.VisitNumber, Is.EqualTo("654321"));
        }

    /// <summary>
    /// Verifies that the repository returns null when searching for an appointment by patient ID and a reason that does not match any existing appointment.
    /// </summary>
    [Test]
        public async Task FindByPatientAndReason_Not_Find_Patient_Return_Null()
        {
            var result = await _repository.FindByPatientAndReason(PatientId, "NotappointmentReason");
    
            Assert.That(result, Is.Null);
        }

    /// <summary>
    /// Tests that FindByPatientAndReason returns a non-null appointment containing the expected AppointmentReason for the specified patient.
    /// </summary>
    [Test]
        public async Task FindByPatientAndReason_Find_Patient_Return_Appointment()
        {
            var result = await _repository.FindByPatientAndReason(PatientId, "appointmentReason");
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result!.AppointmentReason, Is.EqualTo("appointmentReason"));
        }

    /// <summary>
    /// Verifies that <see cref="AppointmentRepository"/>.<c>FindByLocation</c> returns an empty (non-null) collection when no patient is associated with the specified location.
    /// </summary>
    [Test]
        public async Task FindByLocation_Not_Find_Patient_Return_Null()
        {
            var location = new PatientLocation("UCI5C", "Box3");
    
            var result = await _repository.FindByLocation(location);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Count(), Is.EqualTo(0));
        }

    /// <summary>
    /// Verifies that <see cref="AppointmentRepository.FindByLocation"/> successfully retrieves the appointment associated with a patient located at the specified <see cref="PatientLocation"/>.
    /// </summary>
    [Test]
        public async Task FindByLocation_Find_Patient_Return_Appointment()
        {
            var location = new PatientLocation("UCI5C", "Box4");
    
            var result = await _repository.FindByLocation(location);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Count(), Is.EqualTo(1));
        }
}