using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.Masters;
using adas_core.Infrastructure.Repositories;
using Microsoft.Extensions.Options;
using MongoDB.Bson;

namespace adas_core.Test.Repositories;

[TestFixture]
public class MasterListRepositoryTest
{
    /// <summary>
    /// Performs one-time initialization for the test fixture by configuring <see cref="ApiSettings"/> with the expected master list property names, resetting the "MasterLists" collection in the integration database, and creating a <see cref="MasterListRepository{MasterList}"/> instance for use across tests.
    /// </summary>
    [OneTimeSetUp]
        public async Task Init()
        {
            // Set up MongoDB connection
            // var client = new MongoClient("mongodb://localhost:27017");
            // _database = client.GetDatabase("TestDatabase");
    
            // Initialize ApiSettings
            _apiSettings = new ApiSettings
            {
                AltableOptionList = "altableOptionList",
                AllergyList = "allergyList",
                DestinationList = "destinationList",
                DiagnosisList = "diagnosisList",
                DischargeStatusList = "dischargeStatusList",
                DoctorList = "doctorList",
                DoctorTypeList = "doctorTypeList",
                InternalDestinationList = "internalDestinationList",
                InsulationList = "insulationList",
                LanguageBarrierList = "languageBarrierList",
                MobilityOptionList = "mobilityOptionList",
                OriginList = "originList",
                PatientStatusList = "patientStatusList",
                ProcedureList = "procedureList",
                TestList = "testList",
                ServiceList = "serviceList",
                TherapeuticCeilingList = "therapeuticCeilingList",
                TreatmentList = "treatmentList",
                VisitOptionList = "visitOptionList",
                AccessControlList = "accessControlList"
            };
            _optionsApiSettings = Options.Create(_apiSettings);
    
    
            // Insert sample data
            await IntegrationDb.Database.DropCollectionAsync("MasterLists");
            await IntegrationDb.Database.CreateCollectionAsync("MasterLists");
    
            // Create the repository instance
            _repository = new MasterListRepository<MasterList>(_optionsApiSettings, IntegrationDb.Database);
    
            // var masterListCollection = _database.GetCollection<MasterList>("MasterLists");
            // var sampleData = new List<MasterList>
            // {
            //     new MasterList { Id = ObjectId.GenerateNewId(), Name = "Sample MasterList 1" },
            //     new MasterList { Id = ObjectId.GenerateNewId(), Name = "Sample MasterList 2" }
            // };
            // await masterListCollection.InsertManyAsync(sampleData);
        }

    /// <summary>
    /// Performs one-time cleanup after integration test execution by dropping the <c>MasterLists</c> collection from the integration test database to ensure a clean state between test runs.
    /// </summary>
    [OneTimeTearDown]
        public async Task Cleanup()
        {
            // Clean up database after tests
            await IntegrationDb.Database.DropCollectionAsync("MasterLists");
        }

    private MasterListRepository<MasterList> _repository;
    private ApiSettings _apiSettings;
    private IOptions<ApiSettings> _optionsApiSettings;

    /// <summary>
    /// Verifies that <see cref="MasterList"/> entities are correctly persisted in the repository via <c>InsertOneAsync</c>,
    /// by inserting an entity and confirming it can be retrieved by its identifier using the default locale.
    /// </summary>
    [Test]
        public async Task InsertOneAsync_ShouldInsertEntity()
        {
            // Arrange
            var masterList = new MasterList { Id = ObjectId.GenerateNewId(), Name = "Test MasterList" };
    
            // Act
            await _repository.InsertOneAsync(masterList);
            await _repository.GetAll();
            var result = await _repository.FindById(masterList.Id, LocaleEnum.Default);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Name, Is.EqualTo("Test MasterList"));
        }

    /// <summary>
    /// Verifies that the repository's Delete operation successfully removes a MasterList entity, ensuring that a subsequent lookup by id returns no result.
    /// </summary>
    [Test]
        public async Task Delete_ShouldDeleteEntity()
        {
            // Arrange
            var masterList = new MasterList { Id = ObjectId.GenerateNewId(), Name = "ToDelete MasterList" };
            await _repository.InsertOneAsync(masterList);
    
            // Act
            await _repository.Delete(masterList.Id);
            var result = await _repository.FindById(masterList.Id, LocaleEnum.Default);
    
            // Assert
            Assert.That(result, Is.Null);
        }

    /// <summary>
    /// Verifies that the repository's <c>Update</c> method successfully persists changes to an existing <see cref="MasterList"/> entity, allowing the modified record to be retrieved by its identifier with the updated values.
    /// </summary>
    [Test]
        public async Task Update_ShouldUpdateEntity()
        {
            // Arrange
            var masterList = new MasterList { Id = ObjectId.GenerateNewId(), Name = "Original MasterList" };
            await _repository.InsertOneAsync(masterList);
            masterList.Name = "Updated MasterList";
    
            // Act
            await _repository.Update(masterList);
            var result = await _repository.FindById(masterList.Id, LocaleEnum.Default);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Name, Is.EqualTo("Updated MasterList"));
        }

    /// <summary>
    /// Verifies that the repository's FindById method returns the matching entity when a master list with the specified identifier exists in the data store.
    /// </summary>
    /// <returns>A task representing the asynchronous test execution.</returns>
    [Test]
        public async Task FindById_ShouldReturnEntity_WhenFound()
        {
            // Arrange
            var masterList = new MasterList { Id = ObjectId.GenerateNewId(), Name = "FindById MasterList" };
            await _repository.InsertOneAsync(masterList);
    
            // Act
            var result = await _repository.FindById(masterList.Id, LocaleEnum.Default);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Name, Is.EqualTo("FindById MasterList"));
        }

    [Test]
    public async Task FindById_WithDifferentLocale_ShouldReturnEntity_WhenFound()
    {
        // Arrange
        var masterList = new MasterList
        {
            Id = ObjectId.GenerateNewId(), Name = "FindById MasterList",
            DefaultLocale = LocaleEnum.Es,
            Options = new List<OptionList>
            {
                new()
                {
                    Id = ObjectId.GenerateNewId(),
                    Name = "Opción 1",
                    Description = "Descripción 1",
                    LocaleItems = new Locale
                    {
                        Eng = new LocaleItem { Name = "Option eng" },
                        Ca = new LocaleItem { Name = "Opciò cat" },
                        Pt = new LocaleItem { Name = "Opçao por" }
                    }
                }
            }
        };
        await _repository.InsertOneAsync(masterList);

        // Act
        var resultZh = await _repository.FindById(masterList.Id, LocaleEnum.Zh);
        var resultPt = await _repository.FindById(masterList.Id, LocaleEnum.Pt);
        var resultEnUs = await _repository.FindById(masterList.Id, LocaleEnum.Eng);
        var resultCa = await _repository.FindById(masterList.Id, LocaleEnum.Ca);
        var resultEs = await _repository.FindById(masterList.Id, LocaleEnum.Es);

        // Assert ZH return default
        Assert.That(resultZh, Is.Not.Null);
        Assert.That(resultZh?.Options[0].Name, Is.EqualTo("Opción 1"));
        Assert.That(resultZh?.Options[0].LocaleItems, Is.Null);
        // Assert Pt
        Assert.That(resultPt, Is.Not.Null);
        Assert.That(resultPt?.Options[0].Name, Is.EqualTo("Opçao por"));
        Assert.That(resultPt?.Options[0].LocaleItems, Is.Null);
        // Assert Eng
        Assert.That(resultEnUs, Is.Not.Null);
        Assert.That(resultEnUs?.Options[0].Name, Is.EqualTo("Option eng"));
        Assert.That(resultEnUs?.Options[0].LocaleItems, Is.Null);
        // Assert resultCa
        Assert.That(resultCa, Is.Not.Null);
        Assert.That(resultCa?.Options[0].Name, Is.EqualTo("Opciò cat"));
        Assert.That(resultCa?.Options[0].LocaleItems, Is.Null);
        // Assert resultEs
        Assert.That(resultEs, Is.Not.Null);
        Assert.That(resultEs?.Options[0].Name, Is.EqualTo("Opción 1"));
        Assert.That(resultEs?.Options[0].LocaleItems, Is.Null);
    }

    [Test]
    public async Task FindById_WithDifferentLocale_ShouldReturnEntity_WhenFound_AndDefaultLocale()
    {
        // Arrange
        var masterList = new MasterList
        {
            Id = ObjectId.GenerateNewId(), Name = "FindById MasterList",
            DefaultLocale = LocaleEnum.Es,
            Options = new List<OptionList>
            {
                new()
                {
                    Id = ObjectId.GenerateNewId(),
                    Name = "Opción 1",
                    Description = "Descripción 1"
                }
            }
        };
        await _repository.InsertOneAsync(masterList);

        // Act
        var resultZh = await _repository.FindById(masterList.Id, LocaleEnum.Zh);
        var resultPt = await _repository.FindById(masterList.Id, LocaleEnum.Pt);
        var resultEnUs = await _repository.FindById(masterList.Id, LocaleEnum.Eng);
        var resultCa = await _repository.FindById(masterList.Id, LocaleEnum.Ca);
        var resultEs = await _repository.FindById(masterList.Id, LocaleEnum.Es);

        // Assert ZH return default
        Assert.That(resultZh, Is.Not.Null);
        Assert.That(resultZh?.Options[0].Name, Is.EqualTo("Opción 1"));
        // Assert Pt
        Assert.That(resultPt, Is.Not.Null);
        Assert.That(resultPt?.Options[0].Name, Is.EqualTo("Opción 1"));
        // Assert Eng
        Assert.That(resultEnUs, Is.Not.Null);
        Assert.That(resultEnUs?.Options[0].Name, Is.EqualTo("Opción 1"));
        // Assert resultCa
        Assert.That(resultCa, Is.Not.Null);
        Assert.That(resultCa?.Options[0].Name, Is.EqualTo("Opción 1"));
        // Assert resultEs
        Assert.That(resultEs, Is.Not.Null);
        Assert.That(resultEs?.Options[0].Name, Is.EqualTo("Opción 1"));
    }

    /// <summary>
    /// Verifies that the repository's FindByName method returns the matching entity when an entity with the specified name exists in the data store.
    /// </summary>
    [Test]
        public async Task FindByName_ShouldReturnEntity_WhenFound()
        {
            // Arrange
            var masterList = new MasterList { Id = ObjectId.GenerateNewId(), Name = "FindByName MasterList" };
            await _repository.InsertOneAsync(masterList);
    
            // Act
            var result = await _repository.FindByName(masterList.Name);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Name, Is.EqualTo("FindByName MasterList"));
        }

    /// <summary>
    /// Verifies that the repository's GetAll method returns all entities, ensuring the result contains at least two items.
    /// </summary>
    [Test]
        public async Task GetAll_ShouldReturnAllEntities()
        {
            // Act
            var result = await _repository.GetAll();
    
            // Assert
            Assert.That(result.Count(), Is.GreaterThanOrEqualTo(2));
        }

    [Test]
    public async Task GetMasterListByIdAndSearchOptiionsByLocale()
    {
        // Arrange
        var masterList = new MasterList
        {
            Id = ObjectId.GenerateNewId(), Name = "FindById MasterList",
            DefaultLocale = LocaleEnum.Es,
            Options = new List<OptionList>
            {
                new()
                {
                    Id = ObjectId.GenerateNewId(),
                    Name = "Opción 1",
                    Description = "AC0.01",
                    LocaleItems = new Locale
                    {
                        Eng = new LocaleItem { Name = "Option eng" },
                        Ca = new LocaleItem { Name = "Opciò cat" },
                        Pt = new LocaleItem { Name = "Opçao por" }
                    }
                }
            }
        };
        await _repository.InsertOneAsync(masterList);
        var resultPt = await _repository.GetMasterListByIdAndSearchOptions(masterList.Id,
            new FilterOptionListElement { Locale = LocaleEnum.Pt, Text = "Op" });
        Assert.That(resultPt, Is.Not.Null);
        Assert.That(resultPt[0].Name, Is.EqualTo("Opçao por"));
        var resultEng = await _repository.GetMasterListByIdAndSearchOptions(masterList.Id,
            new FilterOptionListElement { Locale = LocaleEnum.Eng, Description = "AC0.01" });
        Assert.That(resultEng, Is.Not.Null);
        Assert.That(resultEng[0].Name, Is.EqualTo("Option eng"));
        var resultEs = await _repository.GetMasterListByIdAndSearchOptions(masterList.Id,
            new FilterOptionListElement { Locale = LocaleEnum.Es, Text = "Op" });
        Assert.That(resultEs, Is.Not.Null);
        Assert.That(resultEs[0].Name, Is.EqualTo("Opción 1"));
    }

    [Test]
    public async Task UpdateMasterListOptionByLocale()
    {
        // Arrange
        var listId = ObjectId.GenerateNewId();
        var optionId = ObjectId.GenerateNewId();
        var masterList = new MasterList
        {
            Id = listId, Name = "FindById MasterList",
            DefaultLocale = LocaleEnum.Es,
            OptionListDetails = new OptionListDetails { Name = new Element { IsRequired = true } },
            Options = new List<OptionList>
            {
                new()
                {
                    Id = optionId,
                    Name = "Opción 1",
                    Description = "AC0.01",
                    LocaleItems = new Locale
                    {
                        Eng = new LocaleItem { Name = "Option eng" },
                        Ca = new LocaleItem { Name = "Opciò cat" },
                        Pt = new LocaleItem { Name = "Opçao por" }
                    }
                }
            }
        };
        await _repository.InsertOneAsync(masterList);
        var optionPt = new OptionList
        {
            Id = optionId,
            Name = "Opçao çambiada"
        };
        var resultPt = await _repository.UpdateMasterListOption(masterList.Id, optionPt, LocaleEnum.Pt);
        Assert.That(resultPt, Is.Not.Null);
        Assert.That(resultPt.Name, Is.EqualTo("Opçao çambiada"));
        var realResult = await _repository.FindById(listId);
        Assert.That(realResult?.Options[0].Name, Is.EqualTo("Opción 1"));
        await _repository.UpdateMasterListOption(masterList.Id, optionPt, LocaleEnum.Pt);
        //         _pointOfCareRepositoryMock.Verify(m => m.FindById(patientLocationId), Times.Once);
    }

    [Test]
    public async Task FindOptionItemByLocaleAndId()
    {
        var masterId = ObjectId.GenerateNewId();
        var optionId = ObjectId.GenerateNewId();
        var masterList = new MasterList
        {
            Id = masterId, Name = "FindById MasterList",
            DefaultLocale = LocaleEnum.Es,
            Options = new List<OptionList>
            {
                new()
                {
                    Id = optionId,
                    Name = "Opción 1",
                    Description = "AC0.01",
                    LocaleItems = new Locale
                    {
                        Eng = new LocaleItem { Name = "Option eng" },
                        Ca = new LocaleItem { Name = "Opciò cat" },
                        Pt = new LocaleItem { Name = "Opçao por" }
                    }
                }
            }
        };
        await _repository.InsertOneAsync(masterList);
        var result = await _repository.FindOptionItemById(masterId, optionId, LocaleEnum.Pt);

        Assert.That(result, Is.Not.Null);
        Assert.That(result.Id!, Is.EqualTo(optionId));
        Assert.That(result.Name, Is.EqualTo("Opçao por"));
    }
}