using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using adas_core.Infrastructure.Repositories;
using adas_core.Test.Utilities;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Moq;
using Options = Microsoft.Extensions.Options.Options;

namespace adas_core.Test.Repositories;

[TestFixture]
[Category("Integration")]
public class PointOfCareRepositoryTests
{
    [OneTimeSetUp]
    public async Task Init()
    {
        _optionsApiSettings = Options.Create(new ApiSettings());
        _mockCollection = new Mock<IMongoCollection<PointOfCare>>();
        _mockDatabase = new Mock<IMongoDatabase>();
        _mockDatabase.Setup(db => db.GetCollection<PointOfCare>(It.IsAny<string>(), null))
            .Returns(_mockCollection.Object);

        await IntegrationDb.Database.DropCollectionAsync("pointOfCares");
        await IntegrationDb.Database.CreateCollectionAsync("pointOfCares");
        _repository = new PointOfCareRepository(_optionsApiSettings, IntegrationDb.Database);
    }

    private PointOfCareRepository _repository;
    private Mock<IMongoCollection<PointOfCare>> _mockCollection;
    private Mock<IMongoDatabase> _mockDatabase;
    private IOptions<ApiSettings> _optionsApiSettings;

    [Test]
    public async Task InsertOneAsync_ValidPointOfCare_InsertsSuccessfully()
    {
        // Arrange
        var pointOfCare = TestUtilities.CreateValidPointOfCare();

        // Act
        await _repository.InsertOneAsync(pointOfCare);

        // Assert
        var insertedPointOfCare = await _repository.FindById(pointOfCare.Id);
        Assert.That(insertedPointOfCare, Is.Not.Null, "Inserted point of care should not be null");
    }

    [Test]
    public async Task Delete_ValidId_DeletesSuccessfully()
    {
        var pointOfCare = TestUtilities.CreateValidPointOfCare();
        await _repository.InsertOneAsync(pointOfCare);

        await _repository.Delete(pointOfCare.Id);

        // Assert
        var deletedPointOfCare = await _repository.FindById(pointOfCare.Id);
        Assert.That(deletedPointOfCare, Is.Null, "Deleted point of care should be null");
    }

    [Test]
    public async Task Update_ValidPointOfCare_UpdatesSuccessfully()
    {
        // Arrange
        var originalPointOfCare = TestUtilities.CreateValidPointOfCare();
        await _repository.InsertOneAsync(originalPointOfCare); // Insert the original point of care

        // Modify some properties to update
        originalPointOfCare.Room = "Updated Room";
        originalPointOfCare.Bed = "Updated Bed";

        // Act
        await _repository.Update(originalPointOfCare); // Update the point of care

        // Retrieve the updated point of care
        var updatedPointOfCare = await _repository.FindById(originalPointOfCare.Id);

        // Assert
        Assert.That(updatedPointOfCare, Is.Not.Null, "Updated point of care should not be null");
        Assert.That(updatedPointOfCare?.Room, Is.EqualTo(originalPointOfCare.Room), "Room should be updated");
        Assert.That(updatedPointOfCare?.Bed, Is.EqualTo(originalPointOfCare.Bed), "Bed should be updated");
    }


    [Test]
    public async Task FindById_ExistingId_ReturnsPointOfCare()
    {
        // Arrange
        var pointOfCareId = ObjectId.GenerateNewId();
        var expectedPointOfCare = TestUtilities.CreateValidPointOfCare();
        expectedPointOfCare.Id = pointOfCareId;

        // Insert a PointOfCare document into the database
        await _repository.InsertOneAsync(expectedPointOfCare);

        // Act
        var result = await _repository.FindById(pointOfCareId);

        // Assert
        Assert.That(result, Is.Not.Null, "Returned PointOfCare should not be null");
        Assert.That(result!.Id, Is.EqualTo(expectedPointOfCare.Id), "Returned PointOfCare should have the expected ID");
        // Add additional assertions to compare other properties if needed
    }

    [Test]
    public async Task FindByUnitAndStatus_ExistingUnitAndStatus_ReturnsMatchingPointOfCares()
    {
        // Arrange
        var expectedPointOfCare = TestUtilities.CreateValidPointOfCare();

        // Insert PointOfCare documents into the database with the specified unit ID and status

        await _repository.InsertOneAsync(expectedPointOfCare);

        // Act
        var result = await _repository.FindByUnitAndStatus(expectedPointOfCare.UnitId, expectedPointOfCare.Status);

        var resultList = result.ToList();
        // Assert
        Assert.That(resultList, Is.Not.Null, "Returned collection should not be null");
        Assert.That(resultList.First().Id, Is.EqualTo(expectedPointOfCare.Id),
            "Returned collection should contain expected PointOfCare object");
    }

    [Test]
    public async Task FindByRoom_ValidRoom_ReturnsMatchingPointOfCares()
    {
        var expectedPointOfCare = TestUtilities.CreateValidPointOfCare();
        await _repository.InsertOneAsync(expectedPointOfCare);

        // Act
        if (expectedPointOfCare.Unit != null)
        {
            var result = await _repository.FindByRoom(expectedPointOfCare.Room);

            var resultList = result?.ToList();

            // Assert
            Assert.That(resultList, Is.Not.Null, "Returned collection should not be null");

            Assert.That(resultList?.Any(p => p.Id == expectedPointOfCare.Id), Is.True,
                "Returned collection should contain expected PointOfCare object");
        }
    }

    [Test]
    public async Task FindByBed_ValidBed_ReturnsMatchingPointOfCares()
    {
        var expectedPointOfCare = TestUtilities.CreateValidPointOfCare();
        await _repository.InsertOneAsync(expectedPointOfCare);

        // Act
        var result = await _repository.FindByBed(expectedPointOfCare.Bed);

        // Convert the result to a list or an array
        var resultList = result?.ToList();

        // Assert
        Assert.That(resultList, Is.Not.Null, "Returned collection should not be null");

        // Check if the expected point of care is contained within the result
        Assert.That(resultList?.First().Bed, Is.EqualTo(expectedPointOfCare.Bed),
            "Returned collection should contain expected PointOfCare object");
    }

    [Test]
    public async Task GetAll_ReturnsAllPointOfCares()
    {
        var poc = TestUtilities.CreateValidPointOfCare();
        await _repository.InsertOneAsync(poc);

        var result = await _repository.GetAll();

        var resultList = result?.ToList();
        // Assert
        Assert.That(resultList?.Any(p => p.Id == poc.Id), Is.True,
            "Returned collection should contain the expected PointOfCare object with the specified Id");
    }
}