using adas_core.Domain.Enums;
using adas_core.Domain.Models;
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 Options = Microsoft.Extensions.Options.Options;

namespace adas_core.Test.Repositories;

[TestFixture]
[Category("Integration")]
public class AdmissionRepositoryTest
{
    [OneTimeSetUp]
    public async Task Init()
    {
        _optionsApiSettings = Options.Create(_apiSettings);


        await IntegrationDb.Database.DropCollectionAsync("admissions");
        await IntegrationDb.Database.CreateCollectionAsync("admissions");

        _repository = new AdmissionRepository(_optionsApiSettings, IntegrationDb.Database);

        var admissions = new List<Admission>
        {
            TestUtilities.CreateValidAdmission(),
            TestUtilities.CreateValidAdmission(),
            TestUtilities.CreateValidAdmission()
        };

        foreach (var admission in admissions) await _repository.InsertOneAsync(admission);
    }

    private AdmissionRepository _repository;
    private IOptions<ApiSettings> _optionsApiSettings;

    private readonly ApiSettings _apiSettings = new()
    {
        Admissions = "admissions"
    };

    [Test]
    public async Task InsertOneAsync_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();

        // Act
        await _repository.InsertOneAsync(admission);

        // Assert
        var result = await _repository.FindById(admission.Id);

        Assert.That(result, Is.Not.Null);
        Assert.That(result?.Id, Is.EqualTo(admission.Id));
    }

    [Test]
    public async Task Delete_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();
        await _repository.InsertOneAsync(admission);

        // Act
        await _repository.Delete(admission.Id);

        // Assert
        var result = await _repository.FindById(admission.Id);
        Assert.That(result, Is.Null);
    }

    [Test]
    public async Task Update_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();
        await _repository.InsertOneAsync(admission);

        // Modify the admission
        admission.DiagnosisAux = "Updated value";

        // Act
        await _repository.Update(admission);

        // Assert
        var updatedAdmission = await _repository.FindById(admission.Id);
        Assert.That(updatedAdmission, Is.Not.Null);
        Assert.That(updatedAdmission?.DiagnosisAux, Is.EqualTo(admission.DiagnosisAux));
    }

    [Test]
    public async Task UpdateLocation_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();
        var originalLocationId = ObjectId.GenerateNewId();
        var newLocationId = ObjectId.GenerateNewId();

        admission.PointOfCareId = originalLocationId;
        await _repository.InsertOneAsync(admission);

        // Act
        await _repository.UpdateLocation(admission.Id, newLocationId);

        // Assert
        var updatedAdmission = await _repository.FindById(admission.Id);
        Assert.That(updatedAdmission, Is.Not.Null);
        Assert.That(newLocationId, Is.EqualTo(updatedAdmission?.PointOfCareId));
    }

    [Test]
    public async Task UpdatePatient_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();
        var newPatient = new Person
        {
            LastName = "Doe",
            FirstName = "John",
            BirthDate = new DateTime(1990, 1, 1),
            Gender = PatientEnum.Gender.Male
        };

        await _repository.InsertOneAsync(admission);

        // Act
        await _repository.UpdatePatient(admission.Id, newPatient);

        // Assert
        var updatedAdmission = await _repository.FindById(admission.Id);
        Assert.That(updatedAdmission, Is.Not.Null);
        Assert.That(updatedAdmission?.Person, Is.Not.Null);
        Assert.That(updatedAdmission?.Person?.FirstName, Is.EqualTo(newPatient.FirstName));
    }

    [Test]
    public async Task FindAll_Success()
    {
        // Arrange


        // Act
        var result = await _repository.FindAll();

        // Assert
        var admissions = result as Admission[] ?? result.ToArray();
        Assert.That(admissions, Is.Not.Null);
        Assert.That(admissions, Is.Not.Empty);
        Assert.That(admissions.Count(), Is.EqualTo(3));
    }

    [Test]
    public async Task FindById_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();
        await _repository.InsertOneAsync(admission);

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

        // Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(admission.Id, Is.EqualTo(result?.Id));
    }

    [Test]
    public async Task FindById_NotFound()
    {
        // Arrange
        var nonExistingId = ObjectId.GenerateNewId();

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

        // Assert
        Assert.That(result, Is.Null);
    }

    [Test]
    public async Task FindByNhc_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();
        await _repository.InsertOneAsync(admission);

        // Act
        var result = await _repository.FindByNhc(admission.Nhc);

        // Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(admission.Nhc, Is.EqualTo(result?.Nhc));
    }

    [Test]
    public async Task FindByNhc_NotFound()
    {
        // Arrange
        var nonExistingNhc = "non-existing-nhc";

        // Act
        var result = await _repository.FindByNhc(nonExistingNhc);

        // Assert
        Assert.That(result, Is.Null);
    }

    // [Test]
    // public async Task FindByLocation_Success()
    // {
    //     // Arrange
    //     var location = new PatientLocation
    //     {
    //         UnitName = "TestUnit",
    //         Bed = "TestBed",
    //         Room = "TestRoom"
    //     };
    //
    //     var admission1 = TestUtilities.CreateValidAdmission();
    //     admission1.PatientLocation = location;
    //
    //     var admission2 = TestUtilities.CreateValidAdmission();
    //     admission2.PatientLocation = location;
    //
    //     await _repository.InsertOneAsync(admission1);
    //     await _repository.InsertOneAsync(admission2);
    //
    //     // Act
    //     var result = await _repository.FindByLocation(location);
    //
    //     // Assert
    //     Assert.That(result, Is.Not.Null);
    //     Assert.That(result, Is.Not.Empty);
    //     Assert.That(result.Count, Is.EqualTo(2));
    //
    //     foreach (var admission in result)
    //     {
    //         Assert.That(admission.PatientLocation, Is.Not.Null);
    //         Assert.That(location.UnitName, Is.EqualTo(admission?.PatientLocation?.UnitName));
    //         Assert.That(location.Bed, Is.EqualTo(admission?.PatientLocation?.Bed));
    //         Assert.That(location.Room, Is.EqualTo(admission?.PatientLocation?.Room));
    //     }
    // }

    // [Test]
    // public async Task FindByOrigin_Success()
    // {
    //     // Arrange
    //     var origin = "TestOrigin";
    //     var admission1 = TestUtilities.CreateValidAdmission();
    //     admission1.Origin = TestUtilities.CreateValidOptionList();
    //
    //     var admission2 = TestUtilities.CreateValidAdmission();
    //     admission2.Origin = TestUtilities.CreateValidOptionList();
    //
    //     await _repository.InsertOneAsync(admission1);
    //     await _repository.InsertOneAsync(admission2);
    //
    //     // Act
    //     var result = await _repository.FindByOrigin(origin);
    //     var resultList = result?.ToList();
    //
    //     // Assert
    //     Assert.That(resultList, Is.Not.Null);
    //     Assert.That(resultList, Is.Not.Empty);
    //     Assert.That(resultList?.Count(), Is.EqualTo(2));
    // }

    [Test]
    public async Task InsertOneAsyncAndReturn_Success()
    {
        // Arrange
        var admission = TestUtilities.CreateValidAdmission();

        // Act
        var result = await _repository.InsertOneAsyncAndReturn(admission);

        // Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result, Is.EqualTo(admission));
        var retrievedAdmission = await _repository.FindById(admission.Id);
        Assert.That(retrievedAdmission, Is.Not.Null);
        Assert.That(retrievedAdmission?.Id, Is.EqualTo(admission.Id));
    }


    [Test]
    public async Task GetAdmissionByUnitIdWithOutPoC_Success()
    {
        // Arrange
        var unitId = ObjectId.GenerateNewId();
        var admission1 = TestUtilities.CreateValidAdmission();
        admission1.UnitId = unitId;
        admission1.PointOfCareId = null;

        var admission2 = TestUtilities.CreateValidAdmission();
        admission2.UnitId = ObjectId.GenerateNewId();
        admission2.PointOfCareId = ObjectId.GenerateNewId();

        await _repository.InsertOneAsync(admission1);
        await _repository.InsertOneAsync(admission2);

        // Act
        var result = await _repository.GetAdmissionByUnitIdWithOutPoC(unitId);

        // Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result.Count, Is.EqualTo(1));
        Assert.That(result[0].UnitId, Is.EqualTo(unitId));
        Assert.That(result[0].PointOfCareId, Is.Null);
    }

    [Test]
    public async Task GetAdmissionByUnitIdWithOutPoC_NoMatch()
    {
        // Arrange
        var unitId = ObjectId.GenerateNewId();

        // Act
        var result = await _repository.GetAdmissionByUnitIdWithOutPoC(unitId);

        // Assert
        Assert.That(result, Is.Not.Null);
        Assert.That(result, Is.Empty);
    }

    [Test]
    public async Task FindByPointOfCareId_Success()
    {
        var pocId = ObjectId.GenerateNewId();
        var admission1 = TestUtilities.CreateValidAdmission();
        admission1.PointOfCareId = pocId;

        var admission2 = TestUtilities.CreateValidAdmission();
        admission2.PointOfCareId = ObjectId.GenerateNewId();

        await _repository.InsertOneAsync(admission1);
        await _repository.InsertOneAsync(admission2);

        var result = await _repository.FindByPointOfCareId(pocId);

        Assert.That(result, Is.Not.Null);
        Assert.That(result.Count, Is.EqualTo(1));
        Assert.That(result[0].PointOfCareId, Is.EqualTo(pocId));
    }

    [Test]
    public async Task FindByPointOfCareId_NoMatch()
    {
        var pocId = ObjectId.GenerateNewId();

        var result = await _repository.FindByPointOfCareId(pocId);

        Assert.That(result, Is.Not.Null);
        Assert.That(result, Is.Empty);
    }

    [Test]
    public async Task FindByDiagnosis_Success()
    {
        var diagnosis = "SomeDiagnosis";
        var admission1 = TestUtilities.CreateValidAdmission();
        admission1.Diagnosis = TestUtilities.CreateValidOptionList();

        var admission2 = TestUtilities.CreateValidAdmission();
        admission2.Diagnosis = TestUtilities.CreateValidOptionList();

        await _repository.InsertOneAsync(admission1);
        await _repository.InsertOneAsync(admission2);

        var result = await _repository.FindByDiagnosis(diagnosis);

        Assert.That(result, Is.Not.Null);
    }

    [Test]
    public async Task FindByDiagnosis_NoMatch()
    {
        var diagnosis = "NonExistentDiagnosis";

        var result = await _repository.FindByDiagnosis(diagnosis);

        Assert.That(result, Is.Empty);
    }
}