using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
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 UnitRepositoryTest
{
    /// <summary>
    /// One-time setup method that prepares the integration test environment by configuring API settings, creating a fresh "units" collection in the database, and seeding it with two sample <see cref="Unit"/> records via the <see cref="UnitRepository"/>.
    /// </summary>
    [OneTimeSetUp]
        public async Task Init()
        {
            // Set up your test environment, including database connection
    
            // Mocking ApiSettings
            _optionsApiSettings = Options.Create(_apiSettings);
    
            // Creating sample unit data
            var unit1 = new Unit
            {
                Id = ObjectId.GenerateNewId(),
                Name = "unit1"
                // Add other properties as needed
            };
    
            var unit2 = new Unit
            {
                Id = ObjectId.GenerateNewId(),
                Name = "unit2"
                // Add other properties as needed
            };
    
            // Insert sample data into the database
            await IntegrationDb.Database.DropCollectionAsync("units");
            await IntegrationDb.Database.CreateCollectionAsync("units");
            _repository = new UnitRepository(_optionsApiSettings, IntegrationDb.Database);
            await _repository.InsertOneUnit(unit1);
            await _repository.InsertOneUnit(unit2);
        }

    private UnitRepository _repository;

    private readonly ApiSettings _apiSettings = new()
    {
        Units = "units"
    };

    private IOptions<ApiSettings> _optionsApiSettings;

    /// <summary>
    /// Verifies that inserting a valid <see cref="Unit"/> into the repository returns the unit and that the inserted unit can be successfully retrieved by its identifier, with key properties preserved.
    /// </summary>
    [Test]
        public async Task InsertOneUnit_ValidUnit_ReturnsUnit()
        {
            // Arrange
            var unit = new Unit
            {
                Id = ObjectId.GenerateNewId(),
                Title = "Test Unit",
                Name = "Test Unit",
                LastUpdate = DateTime.UtcNow,
                PointOfCareIds = [ObjectId.GenerateNewId()],
                Status = StatusEnum.Type.Ok,
                Configuration = new UnitConfiguration
                {
                    AutoAdt = true,
                },
                AltableOptionListId = ObjectId.GenerateNewId(),
                AllergyListId = ObjectId.GenerateNewId()
            };
    
            // Act
            var insertResult = await _repository.InsertOneUnit(unit);
            var findResult = await _repository.FindById(unit.Id);
    
            // Assert
            Assert.That(insertResult, Is.Not.Null);
            Assert.That(findResult, Is.Not.Null);
            Assert.That(findResult?.Id, Is.EqualTo(unit.Id));
            Assert.That(findResult?.Title, Is.EqualTo(unit.Title));
            Assert.That(findResult?.Name, Is.EqualTo(unit.Name));
        }

    // [Test]
    // public async Task FindByLocation_ValidLocation_ReturnsUnit()
    // {
    //     // Arrange
    //     var location = new PatientLocation
    //     {
    //         Bed = "TestBed",
    //         UnitName = "TestUnit"
    //     };
    //
    //     var unit = new Unit
    //     {
    //         Id = ObjectId.GenerateNewId(),
    //         Title = "Test Unit",
    //         Name = "Test Unit",
    //         PointOfCares = new List<PointOfCare>
    //         {
    //             new PointOfCare { Bed = "TestBed", UnitName = "TestUnit" }
    //         }
    //     };
    //
    //     // Insert the unit into the MongoDB database
    //     await _repository.InsertOneUnit(unit);
    //
    //     // Act
    //     var result = await _repository.FindByLocation(location);
    //
    //     // Assert
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result?.Id, Is.EqualTo(unit.Id));
    // }

    /// <summary>
    /// Verifies that FindById retrieves the matching <see cref="Unit"/> from the repository when a valid identifier is provided.
    /// </summary>
    [Test]
        public async Task FindById_ValidId_ReturnsUnit()
        {
            // Arrange
            var unitId = ObjectId.GenerateNewId();
            var unit = new Unit
            {
                Id = unitId,
                Title = "Test Unit",
                Name = "Test Unit"
            };
    
            await _repository.InsertOneUnit(unit);
    
            // Act
            var result = await _repository.FindById(unitId);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Id, Is.EqualTo(unitId));
        }

    /// <summary>
    /// Verifies that FindById returns <c>null</c> when queried with an ID that does not exist in the repository.
    /// </summary>
    [Test]
        public async Task FindById_InvalidId_ReturnsNull()
        {
            // Arrange
            var invalidId = ObjectId.GenerateNewId();
    
            // Act
            var result = await _repository.FindById(invalidId);
    
            // Assert
            Assert.That(result, Is.Null);
        }

    // [Test]
    // public async Task FindByMasterListId_ValidId_ReturnsMatchingUnits()
    // {
    //     // Arrange
    //     var objectId = ObjectId.GenerateNewId();
    //     var masterListType = MasterListType.DestinationList;
    //
    //     // Insert units associated with the given ObjectId and MasterListType
    //     var units = new List<Unit>
    //     {
    //         new Unit
    //         {
    //             Id = ObjectId.GenerateNewId(),
    //             // Set other properties
    //             DestinationList = objectId // Set property dynamically based on masterListType
    //         },
    //         new Unit
    //         {
    //             Id = ObjectId.GenerateNewId(),
    //             // Set other properties
    //             DestinationList = ObjectId.GenerateNewId() // Different ObjectId
    //         },
    //         // Add more units as needed
    //     };
    //
    //     foreach (var unit in units)
    //     {
    //         await _repository.InsertOneUnit(unit);
    //     }
    //
    //     // Act
    //     var result = await _repository.FindByMasterListId(objectId, masterListType);
    //
    //     // Assert
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result,
    //         Has.All.Property("SomeTypeId").EqualTo(objectId)); // Check if all returned units have the expected ObjectId
    //     // Add more assertions as needed
    // }

    /// <summary>
    /// Verifies that the repository's <c>FindByName</c> method returns the matching unit when called with a valid unit name.
    /// The test inserts a unit, retrieves it by name, and asserts that the returned entity is not null and has the expected name.
    /// </summary>
    [Test]
        public async Task FindByName_ValidName_ReturnsUnit()
        {
            // Arrange
            var unitName = "TestUnit";
            var unit = new Unit
            {
                Id = ObjectId.GenerateNewId(),
                Title = "Test Unit",
                Name = unitName
            };
    
            await _repository.InsertOneUnit(unit);
    
            // Act
            var result = await _repository.FindByName(unitName);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Name, Is.EqualTo(unitName));
        }

    /// <summary>
    /// Verifies that the repository's FindByName method returns null when invoked with a name that does not match any existing entity.
    /// </summary>
    [Test]
        public async Task FindByName_InvalidName_ReturnsNull()
        {
            // Arrange
            var invalidName = "NonExistentUnitName";
    
            // Act
            var result = await _repository.FindByName(invalidName);
    
            // Assert
            Assert.That(result, Is.Null);
        }

    // [Test]
    // public async Task FindByPointOfCare_ValidPointOfCare_ReturnsUnit()
    // {
    //     // Arrange
    //     var pointOfCare = new PointOfCare
    //     {
    //         Bed = "TestBed",
    //         Room = "TestRoom",
    //         UnitName = "TestUnit"
    //     };
    //
    //     var unit = new Unit
    //     {
    //         Id = ObjectId.GenerateNewId(),
    //         Title = "Test Unit",
    //         Name = "Test Unit",
    //         PointOfCares = new System.Collections.Generic.List<PointOfCare>
    //         {
    //             pointOfCare
    //         }
    //     };
    //
    //     await _repository.InsertOneUnit(unit);
    //
    //     // Act
    //     var result = await _repository.FindByPointOfCare(pointOfCare);
    //
    //     // Assert
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result?.PointOfCares, Contains.Item(pointOfCare));
    // }

    // [Test]
    // public async Task FindByPointOfCare_InvalidPointOfCare_ReturnsNull()
    // {
    //     // Arrange
    //     var invalidPointOfCare = new PointOfCare
    //     {
    //         Bed = "NonExistentBed",
    //         Room = "NonExistentRoom",
    //         UnitName = "NonExistentUnitName"
    //     };
    //
    //     // Act
    //     var result = await _repository.FindByPointOfCare(invalidPointOfCare);
    //
    //     // Assert
    //     Assert.That(result, Is.Null);
    // }

    // [Test]
    // public async Task FindByUnitName_ValidUnitName_ReturnsMatchingUnits()
    // {
    //     // Arrange
    //     var unitName = "TestUnit";
    //
    //     var units = new List<Unit>
    //     {
    //         new Unit
    //         {
    //             Id = ObjectId.GenerateNewId(),
    //             Name = unitName,
    //         },
    //         new Unit
    //         {
    //             Id = ObjectId.GenerateNewId(),
    //             Name = unitName,
    //         },
    //     };
    //
    //     foreach (var unit in units)
    //     {
    //         await _repository.InsertOneUnit(unit);
    //     }
    //
    //     // Act
    //     var result = await _repository.FindByUnitName(unitName);
    //
    //     // Assert
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result, Has.Count.EqualTo(units.Count));
    // }

    // [Test]
    // public async Task FindByUnitName_InvalidUnitName_ReturnsEmptyList()
    // {
    //     // Arrange
    //     var invalidUnitName = "NonExistentUnitName";
    //
    //     // Act
    //     var result = await _repository.FindByUnitName(invalidUnitName);
    //
    //     // Assert
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result, Is.Empty);
    // }

    /// <summary>
    /// Verifies that <c>GetAll</c> returns a non-null collection containing the units that have been inserted into the repository.
    /// </summary>
    /// <exception cref="ArgumentNullException">Thrown when the <c>units</c> collection used to seed the repository is <c>null</c>.</exception>
    [Test]
        public async Task GetAll_ReturnsAllUnits()
        {
            // Arrange
    
            var units = new List<Unit>();
            if (units == null) throw new ArgumentNullException(nameof(units));
            for (var i = 0; i < 5; i++)
            {
                var unit = new Unit
                {
                    Id = ObjectId.GenerateNewId()
                };
                units.Add(unit);
                await _repository.InsertOneUnit(unit);
            }
    
            // Act
            var result = await _repository.GetAll();
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result, Has.Count.Not.EqualTo(0));
        }

    /// <summary>
    /// Verifies that UpdateUnit correctly updates an existing unit in the repository and returns the updated entity with the new title and the original identifier.
    /// </summary>
    [Test]
        public async Task UpdateUnit_ValidUnit_ReturnsUpdatedUnit()
        {
            // Arrange
            var originalUnit = new Unit
            {
                Id = ObjectId.GenerateNewId(),
                Title = "Original Title"
            };
    
            var updatedUnit = new Unit
            {
                Id = originalUnit.Id,
                Title = "Updated Title"
            };
    
            await _repository.InsertOneUnit(originalUnit);
    
            // Act
            var result = await _repository.UpdateUnit(updatedUnit);
    
            // Assert
            Assert.That(result, Is.Not.Null);
            Assert.That(result?.Id, Is.EqualTo(originalUnit.Id));
            Assert.That(result?.Title, Is.EqualTo(updatedUnit.Title));
        }
}