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

namespace adas_core.Test.Repositories;

[TestFixture]
[Category("Integration")]
public class PatientRepositoryTest
{
    //private static readonly ObjectId id = ObjectId.GenerateNewId();

    /// <summary>
    /// One-time setup that prepares the integration test database by recreating the
    /// <c>patient</c>, <c>pointOfCares</c>, <c>units</c>, and <c>list_origin</c> collections
    /// and seeding them with five patients, their associated point of care, unit, and origin
    /// list records, then verifies that the initial bulk insert yields exactly five patient
    /// documents.
    /// </summary>
    [OneTimeSetUp]
        public async Task Init()
        {
            _optionsApiSettings = Options.Create(_apiSettings);
    
            var patient1 = new Patient
            {
                Id = PatientId,
                PatientId = "patientId1",
                UnitId = _unit1,
                PointOfCareId = _poc1.Id,
                PatientNumber = "patientNumber1",
                Person = new Person
                {
                    FirstName = "firstName1",
                    LastName = "lastName1",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                },
                DisTime = Now,
                Origin = new OptionList
                {
                    Id = OriginListOptionId,
                    Name = "Urlogy"
                }
            };
    
            var patient2 = new Patient
            {
                UnitId = _unit1,
                PatientId = "patientId2",
                PointOfCareId = _poc2.Id,
                PatientNumber = "patientNumber2",
                Person = new Person
                {
                    FirstName = "firstName2",
                    LastName = "lastName2",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
    
            var patient3 = new Patient
            {
                UnitId = _unit1,
                PatientId = "patientId3",
                PointOfCareId = _poc3.Id,
                PatientNumber = "patientNumber3",
                Person = new Person
                {
                    FirstName = "firstName3",
                    LastName = "lastName3",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
            var patient4 = new Patient
            {
                UnitId = _pocMoved.UnitId,
                PatientId = "patientId4",
                PointOfCareId = _pocMoved.Id,
                PatientNumber = "patientNumber4",
                Person = new Person
                {
                    FirstName = "firstName4",
                    LastName = "lastName4",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
            var patient5 = new Patient
            {
                UnitId = _pocPushed.UnitId,
                PatientId = "patientId5",
                PointOfCareId = _pocPushed.Id,
                PatientNumber = "patientNumber5",
                Person = new Person
                {
                    FirstName = "firstName5",
                    LastName = "lastName5",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
    
            await IntegrationDb.Database.DropCollectionAsync("patient");
            await IntegrationDb.Database.CreateCollectionAsync("patient");
            await IntegrationDb.Database.DropCollectionAsync("pointOfCares");
            await IntegrationDb.Database.CreateCollectionAsync("pointOfCares");
            await IntegrationDb.Database.DropCollectionAsync("units");
            await IntegrationDb.Database.CreateCollectionAsync("units");
            await IntegrationDb.Database.DropCollectionAsync("list_origin");
            await IntegrationDb.Database.CreateCollectionAsync("list_origin");
    
            _repository = new PatientRepository(_optionsApiSettings, IntegrationDb.Database);
            _repositoryPoc = new PointOfCareRepository(_optionsApiSettings, IntegrationDb.Database);
            _repositoryUnit = new UnitRepository(_optionsApiSettings, IntegrationDb.Database);
            _repositoryList = new MasterListRepository<OriginList>(_optionsApiSettings, IntegrationDb.Database);
    
            await _repositoryPoc.InsertOneAsync(_poc1);
            await _repositoryPoc.InsertOneAsync(_poc2);
            await _repositoryPoc.InsertOneAsync(_poc3);
            await _repositoryPoc.InsertOneAsync(_pocMoved);
            await _repositoryPoc.InsertOneAsync(_pocPushed);
            await _repository.InsertOneAsync(patient1);
            await _repository.InsertOneAsync(patient2);
            await _repository.InsertOneAsync(patient3);
            await _repository.InsertOneAsync(patient4);
            await _repository.InsertOneAsync(patient5);
            await _repositoryUnit.InsertOneAsync(_unit);
            await _repositoryList.InsertOneAsync(_originList);
    
            await _repository.FindByPatientNumber("patientNumber1");
    
            await _repository.InsertManyAsync([patient1, patient2, patient3, patient4, patient5]);
            Assert.That(await _repository.Collection.CountDocumentsAsync(_ => true), Is.EqualTo(5));
        }

    private PatientRepository _repository;
    private PointOfCareRepository _repositoryPoc;
    private UnitRepository _repositoryUnit;
    private MasterListRepository<OriginList> _repositoryList;

    private readonly ApiSettings _apiSettings = new()
    {
        ArchivePatientsDiagnosis = "patients"
    };

    private IOptions<ApiSettings> _optionsApiSettings;

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

    private readonly ObjectId _unit1 = new("662760a96bdc7150b49fe29c");

    private readonly Unit _unit = new()
    {
        Id = ObjectId.Parse("662760a96bdc7150b49fe29c"),
        Name = "unit1",
        OriginListId = OriginListId
    };

    private readonly OriginList _originList = new()
    {
        Id = OriginListId,
        Name = "OriginList",
        Options = new List<OptionList>
        {
            new()
            {
                Id = OriginListOptionId,
                Name = "Urlogy",
                LocaleItems = new Locale
                {
                    Es = new LocaleItem { Name = "Urologia" }
                }
            }
        },
        CanAddElement = true,
        ListType = MasterListType.OriginList,
        DefaultLocale = LocaleEnum.Eng
    };

    private readonly PointOfCare _poc1 = new()
    {
        Bed = "Bed1",
        Id = ObjectId.GenerateNewId(),
        Room = "Room1",
        UnitId = ObjectId.GenerateNewId()
    };

    private readonly PointOfCare _poc2 = new()
    {
        Bed = "Bed1",
        Id = ObjectId.GenerateNewId(),
        Room = "Room1",
        UnitId = ObjectId.GenerateNewId()
    };

    private readonly PointOfCare _poc3 = new()
    {
        Bed = "Bed1",
        Id = ObjectId.GenerateNewId(),
        Room = "Room1",
        UnitId = ObjectId.GenerateNewId()
    };

    private readonly PointOfCare _pocMoved = new()
    {
        Bed = VirtualPointOfCare.Moved.ToString(),
        Id = ObjectId.GenerateNewId(),
        Room = VirtualPointOfCare.Moved.ToString(),
        UnitId = ObjectId.GenerateNewId()
    };

    private readonly PointOfCare _pocPushed = new()
    {
        Bed = VirtualPointOfCare.Pushed.ToString(),
        Id = ObjectId.GenerateNewId(),
        Room = VirtualPointOfCare.Pushed.ToString(),
        UnitId = ObjectId.GenerateNewId()
    };

    /// <summary>
    /// Verifies that _repository.FindInActivePoC returns a non-null result containing exactly three inactive PoC entries.
    /// </summary>
    [Test]
        public async Task FindInActivePoC()
        {
            var result = await _repository.FindInActivePoC();
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Count, Is.EqualTo(3));
        }

    /// <summary>
    /// Verifies that _repository.FindInInactivePoC() returns a non-null collection containing exactly two inactive PoC records.
    /// </summary>
    /// <returns>A Task representing the asynchronous test execution.</returns>
    [Test]
        public async Task FindInInactivePoC()
        {
            var result = await _repository.FindInInactivePoC();
            Assert.That(result, Is.Not.Null);
            Assert.That(result.Count, Is.EqualTo(2));
        }

    /// <summary>
    /// Verifies that FindById returns the expected <c>Patient</c> when a valid patient identifier is provided, ensuring the result is not null and the <c>PatientNumber</c> matches the expected value.
    /// </summary>
    [Test]
        public async Task FindById_Find_Return_Patient()
        {
            var result = await _repository.FindById(PatientId);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result.PatientNumber, Is.EqualTo("patientNumber1"));
        }

    /// <summary>
    /// Verifies that the repository's <c>FindByPatientNumber</c> method returns <c>null</c> when invoked with an empty patient number, confirming the not-found scenario for blank input.
    /// </summary>
    [Test]
        public async Task FindByPatientNumber_Not_Find_Return_Patient()
        {
            var result = await _repository.FindByPatientNumber("");
    
            Assert.That(result, Is.Null);
        }

    /// <summary>
    /// Verifies that the repository's <c>FindByPatientNumber</c> method successfully retrieves
    /// a patient when called with a valid patient number, returning a non-null result
    /// with a matching <c>PatientNumber</c>.
    /// </summary>
    [Test]
        public async Task FindByPatientNumber_Find_Return_Patient()
        {
            var result = await _repository.FindByPatientNumber("patientNumber1");
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result.PatientNumber, Is.EqualTo("patientNumber1"));
        }

    /// <summary>
    /// Verifies that the repository's <c>FindAll</c> method returns a non-null list containing all expected patient records (five entries).
    /// </summary>
    [Test]
        public async Task FindAll_Return_List_Patients()
        {
            var result = await _repository.FindAll();
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Has.Count.EqualTo(5));
        }

    /// <summary>
    /// Verifies that the repository's <c>Update</c> method successfully updates an existing patient record
    /// by confirming that the updated <c>PatientNumber</c> becomes findable after the update, while ensuring
    /// the new value is not present prior to the update operation. Also validates that the updated record
    /// is persisted with the modified value and that the patient can be cleaned up via <c>Delete</c>.
    /// </summary>
    [Test]
        public async Task Update_with_base_UpdateOneAsync()
        {
            var patient = new Patient
            {
                Id = ObjectId.GenerateNewId(),
                UnitString = "PointOfCare",
                Bed = "bed",
                PatientNumber = "patientNumber",
                Person = new Person
                {
                    FirstName = "firstName",
                    LastName = "lastName",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
    
            var patientUpdate = new Patient
            {
                Id = patient.Id,
                UnitString = "PointOfCare",
                Bed = "bed",
                PatientNumber = "patientNumberUpdate",
                Person = new Person
                {
                    FirstName = "firstName",
                    LastName = "lastName",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
    
            await _repository.InsertOneAsync(patient);
    
            var result = await _repository.FindByPatientNumber("patientNumberUpdate");
    
            Assert.That(result, Is.Null);
    
            await _repository.Update(patientUpdate);
    
            var resultUpdate = await _repository.FindByPatientNumber("patientNumberUpdate");
    
            Assert.That(resultUpdate, Is.Not.Null);
            Assert.That(resultUpdate.PatientNumber, Is.EqualTo("patientNumberUpdate"));
    
            await _repository.Delete(patient.Id);
        }

    /// <summary>
    /// Verifies that a patient can be successfully deleted from the repository by inserting a patient,
    /// confirming it exists, deleting it, and then confirming it is no longer retrievable.
    /// </summary>
    [Test]
        public async Task Delete()
        {
            var patientDelete = new Patient
            {
                Id = ObjectId.GenerateNewId(),
                UnitString = "PointOfCare",
                Bed = "bedDelete",
                PatientNumber = "patientNumberDelete",
                Person = new Person
                {
                    FirstName = "firstName",
                    LastName = "lastName",
                    BirthDate = new DateTime(),
                    Gender = PatientEnum.Gender.Male
                }
            };
    
            await _repository.InsertOneAsync(patientDelete);
    
            var result = await _repository.FindByPatientNumber("patientNumberDelete");
    
            Assert.That(result, Is.Not.Null);
    
            await _repository.Delete(patientDelete.Id);
    
            var resultDelete = await _repository.FindByPatientNumber("patientNumberDelete");
    
            Assert.That(resultDelete, Is.Null);
        }


    // [Test]
    // public async Task UpdateLocation()
    // {
    //     var patient = new Patient()
    //     {
    //         Id = ObjectId.GenerateNewId(),
    //         UnitId = _unit1,
    //         PointOfCareId = ObjectId.GenerateNewId(),
    //         PatientNumber = "patientNumberLocation",
    //         Person = new Person
    //         {
    //             FirstName = "firstName",
    //             LastName = "lastName",
    //             BirthDate = new DateTime(),
    //             Gender = Gender.Male,
    //         }
    //     };
    //
    //     var location = new PatientLocation("PointOfCare", "BedLocationUpdate");
    //
    //     await _repository.InsertOneAsync(patient);
    //     
    //     var result = await _repository.FindByPointOfCare(_poc1.Id);
    //
    //     // Assert.That(result, Is.Null);
    //
    //     await _repository.UpdateLocation(result.FirstOrDefault().Id,location);
    //
    //     var resultUpdate = await _repository.FindByPointOfCare(_poc1.Id);
    //
    //     Assert.That(resultUpdate, Is.Not.Null);
    //     Assert.That(resultUpdate.FirstOrDefault().Bed, Is.EqualTo("BedLocationUpdate"));
    //
    //     await _repository.Delete(patient.Id);
    // }

    /// <summary>
    /// Tests that the attending doctor of a point of care entry can be successfully updated with a new doctor.
    /// Verifies that after the update, the entry retrieved by point of care reflects the new attending doctor's information.
    /// </summary>
    [Test]
        public async Task UpdateAttendingDoctor()
        {
            var doctor = new Person
            {
                FirstName = "firstNameDoctor2",
                LastName = "lastNameDoctor2",
                Gender = PatientEnum.Gender.Female
            };
    
            var result = await _repository.FindByPointOfCare(_poc1.Id);
    
            await _repository.UpdateAttendingDoctor(result.First().Id, doctor);
    
            var resultUpdate = await _repository.FindByPointOfCare(_poc1.Id);
    
            Assert.That(resultUpdate, Is.Not.Null);
            Assert.That(resultUpdate.First().AttendingDoctor?.FirstName, Is.EqualTo("firstNameDoctor2"));
        }

    // [Test]
    // public async Task UpdatePatientData_UpdatePatientNumber_true()
    // {
    //     var patient1 = new Person
    //     {
    //         FirstName = "firstNamePatient1",
    //         LastName = "lastNamePatient1",
    //         BirthDate = new DateTime(),
    //         Gender = Gender.Male
    //     };
    //
    //     var patient2 = new Person
    //     {
    //         FirstName = "firstNamePatient2",
    //         LastName = "lastNamePatient2",
    //         BirthDate = new DateTime(),
    //         Gender = Gender.Female
    //     };
    //
    //     var patient = new Patient()
    //     {
    //         Id = ObjectId.GenerateNewId(),
    //         UnitId = _unit1,
    //         PointOfCareId = ObjectId.GenerateNewId(),
    //         PatientNumber = "patientNumber",
    //         Person = patient1
    //     };
    //
    //     var location = new PatientLocation("PointOfCare", "bed");
    //
    //     await _repository.InsertOneAsync(patient);
    //
    //     var result = await _repository.FindByLocation(location);
    //
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result.Person?.FirstName, Is.EqualTo("firstNamePatient1"));
    //
    //     await _repository.UpdatePatientData(patient.Id, "patientNumberUpdate", patient2, true);
    //
    //     var resultUpdate = await _repository.FindByLocation(location);
    //
    //     Assert.That(resultUpdate, Is.Not.Null);
    //     Assert.That(resultUpdate.Person?.FirstName, Is.EqualTo("firstNamePatient2"));
    //
    //     await _repository.Delete(patient.Id);
    // }

    // [Test]
    // public async Task UpdatePatientData_UpdatePatientNumber_false()
    // {
    //     var patient1 = new Person
    //     {
    //         FirstName = "firstNamePatient1",
    //         LastName = "lastNamePatient1",
    //         BirthDate = new DateTime(),
    //         Gender = Gender.Male
    //     };
    //
    //     var patient2 = new Person
    //     {
    //         FirstName = "firstNamePatient2",
    //         LastName = "lastNamePatient2",
    //         BirthDate = new DateTime(),
    //         Gender = Gender.Female
    //     };
    //
    //     var patient = new Patient()
    //     {
    //         Id = ObjectId.GenerateNewId(),
    //         UnitId = _unit1,
    //         PointOfCareId = ObjectId.GenerateNewId(),
    //         PatientNumber = "patientNumber",
    //         Person = patient1
    //     };
    //
    //     var location = new PatientLocation("PointOfCare", "bed");
    //
    //     await _repository.InsertOneAsync(patient);
    //
    //     var result = await _repository.FindByLocation(location);
    //
    //     Assert.That(result, Is.Not.Null);
    //     Assert.Multiple(() =>
    //     {
    //         Assert.That(result.Person?.FirstName, Is.EqualTo("firstNamePatient1"));
    //         Assert.That(result.PatientNumber, Is.EqualTo("patientNumber"));
    //     });
    //     await _repository.UpdatePatientData(patient.Id, "patientNumberUpdate", patient2, false);
    //
    //     var resultUpdate = await _repository.FindByLocation(location);
    //
    //     Assert.That(resultUpdate, Is.Not.Null);
    //     Assert.Multiple(() =>
    //     {
    //         Assert.That(resultUpdate.Person?.FirstName, Is.EqualTo("firstNamePatient2"));
    //         Assert.That(result.PatientNumber, Is.EqualTo("patientNumber"));
    //     });
    //     await _repository.Delete(patient.Id);
    // }

    // [Test]
    // public async Task UpdateOne()
    // {
    //     var person1 = new Person
    //     {
    //         FirstName = "firstNamePatient1",
    //         LastName = "lastNamePatient1",
    //         BirthDate = new DateTime(),
    //         Gender = Gender.Male
    //     };
    //
    //     var person2 = new Person
    //     {
    //         FirstName = "firstNamePatient2",
    //         LastName = "lastNamePatient2",
    //         BirthDate = new DateTime(),
    //         Gender = Gender.Female
    //     };
    //
    //     var patient1 = new Patient()
    //     {
    //         Id = ObjectId.GenerateNewId(),
    //         UnitId = _unit1,
    //         PointOfCareId = ObjectId.GenerateNewId(),
    //         PatientNumber = "patientNumber",
    //         Person = person1
    //     };
    //
    //     var patient2 = new Patient()
    //     {
    //         Id = patient1.Id,
    //         UnitId = _unit1,
    //         PointOfCareId = ObjectId.GenerateNewId(),
    //         PatientNumber = "patientNumberUpdate",
    //         Person = person2
    //     };
    //
    //     var location1 = new PatientLocation("PointOfCare", "bed");
    //     var location2 = new PatientLocation("PointOfCare2", "bed2");
    //
    //     await _repository.InsertOneAsync(patient1);
    //
    //     var result = await _repository.FindByLocation(location1);
    //
    //     Assert.That(result, Is.Not.Null);
    //     Assert.Multiple(() =>
    //     {
    //         Assert.That(result.Person?.FirstName, Is.EqualTo("firstNamePatient1"));
    //         Assert.That(result.PatientNumber, Is.EqualTo("patientNumber"));
    //     });
    //     await _repository.UpdateOne(patient2);
    //
    //     var resultLocation1 = await _repository.FindByLocation(location1);
    //
    //     Assert.That(resultLocation1, Is.Null);
    //
    //     var resultLocation2 = await _repository.FindByLocation(location2);
    //
    //     Assert.That(resultLocation2, Is.Not.Null);
    //     Assert.Multiple(() =>
    //     {
    //         Assert.That(resultLocation2.Person?.FirstName, Is.EqualTo("firstNamePatient2"));
    //         Assert.That(resultLocation2.PatientNumber, Is.EqualTo("patientNumberUpdate"));
    //     });
    //     await _repository.Delete(patient1.Id);
    // }

    /// <summary>
    /// Verifies that _repository.FindByPatientId returns <c>null</c> when no patient is found for the provided patient identifier.
    /// </summary>
    [Test]
        public async Task FindByPatientId_Not_Find_Return_Patient()
        {
            var result = await _repository.FindByPatientId("");
    
            Assert.That(result, Is.Null);
        }

    // [Test]
    // public async Task FindByPatientId_Find_Return_Patient()
    // {
    //     var result = await _repository.FindByPatientId(_poc1.Patient.Id);
    //
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result.PatientNumber, Is.EqualTo("patientNumber1"));
    // }

    /// <summary>
    /// Verifies that the repository returns a non-null and non-empty collection of patients when searching by the identifier of an existing point of care.
    /// </summary>
    [Test]
        public async Task FindByPointOfCare_Find_Return_Patients()
        {
            var result = await _repository.FindByPointOfCare(_poc1.Id);
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Not.Empty);
            // Assert.That(result.Count.Equals(1));
        }

    /// <summary>
    /// Verifies that _repository.FindByPointOfCare returns a non-null empty collection when no patients match the provided point of care identifier.
    /// </summary>
    [Test]
        public async Task FindByPointOfCare_Not_Find_Return_Patients()
        {
            var result = await _repository.FindByPointOfCare(ObjectId.GenerateNewId());
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Empty);
        }

    /// <summary>
    /// Verifies that the repository returns a non-null and non-empty collection of discharged patients when calling <c>FindDischargedPatients</c>.
    /// </summary>
    [Test]
        public async Task FindDischargedPatients_Find_Return_Patients()
        {
            var result = await _repository.FindDischargedPatients();
    
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Is.Not.Empty);
        }
}