using System.Security.Claims;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Masters;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using Moq;

namespace adas_core.Test.Services;

[TestFixture]
public class MasterListServiceTest
{
    /// <summary>
    /// Initializes all required mock dependencies and constructs a <see cref="MasterListService{MasterList}"/> instance
    /// for use in unit tests. Configures the HTTP context with a test user principal, registers the master list repository
    /// in the service provider, and supplies default API settings.
    /// </summary>
    [SetUp]
    public void Setup()
    {
        _loggerMock = new Mock<ILogger<MasterListService<MasterList>>>();
        _serviceProviderMock = new Mock<IServiceProvider>();
        _clientMessageServiceMock = new Mock<IClientMessageService>();
        _subscribersServiceMock = new Mock<ISubscribersService>();
        _unitServiceMock = new Mock<IUnitService>();
        _displayServiceMock = new Mock<IDisplayService>();
        _repositoryMock = new Mock<IMasterListRepository<MasterList>>();
        _patientServiceMock = new Mock<IPatientService>();
        _dischargeServiceMock = new Mock<IDischargeService>();
        _admissionServiceMock = new Mock<IAdmissionService>();
        _apiSettingsMock = new Mock<IOptions<ApiSettings>>();
        var userClaims = new ClaimsPrincipal(new ClaimsIdentity([
            new Claim(ClaimTypes.Name, "TestUser")
        ], "mock"));

        var httpContextMock = new DefaultHttpContext
        {
            User = userClaims
        };

        _httpContextAccessorMock.Setup(accessor => accessor.HttpContext)
            .Returns(httpContextMock);
        var apiSettings = new ApiSettings
        {
            PathToDisplayAssets = ["path/to/assets"]
        };

        _apiSettingsMock.Setup(ap => ap.Value).Returns(apiSettings);

        _serviceProviderMock.Setup(sp => sp.GetService(typeof(IMasterListRepository<MasterList>)))
            .Returns(_repositoryMock.Object);

        _service = new MasterListService<MasterList>(
            _loggerMock.Object,
            _serviceProviderMock.Object,
            new Lazy<IClientMessageService>(() => _clientMessageServiceMock.Object),
            _subscribersServiceMock.Object,
            new Lazy<IUnitService>(() => _unitServiceMock.Object),
            new Lazy<IDisplayService>(() => _displayServiceMock.Object),
            new Lazy<IPatientService>(() => _patientServiceMock.Object),
            new Lazy<IDischargeService>(() => _dischargeServiceMock.Object),
            new Lazy<IAdmissionService>(() => _admissionServiceMock.Object),
            _apiSettingsMock.Object,
            _httpContextAccessorMock.Object,
            _auditServiceMock.Object
        );
    }

    private Mock<ILogger<MasterListService<MasterList>>> _loggerMock = null!;
    private Mock<IServiceProvider> _serviceProviderMock = null!;
    private Mock<IClientMessageService> _clientMessageServiceMock = null!;
    private Mock<ISubscribersService> _subscribersServiceMock = null!;
    private Mock<IUnitService> _unitServiceMock = null!;
    private Mock<IDisplayService> _displayServiceMock = null!;
    private Mock<IPatientService> _patientServiceMock = null!;
    private Mock<IDischargeService> _dischargeServiceMock = null!;
    private Mock<IAdmissionService> _admissionServiceMock = null!;
    private Mock<IMasterListRepository<MasterList>> _repositoryMock = null!;
    private Mock<IOptions<ApiSettings>> _apiSettingsMock = null!;
    private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock = new();
    private readonly Mock<ILocalAuditService> _auditServiceMock = new();
    private MasterListService<MasterList> _service = null!;


    /// <summary>
    /// Verifies that <c>DeleteMasterListById</c> invokes the repository's <c>Delete</c> method once with the provided identifier when the master list is found.
    /// </summary>
    [Test]
    public async Task DeleteMasterListById_ShouldCallRepositoryDelete_WhenMasterListFound()
    {
        // Arrange
        var id = ObjectId.GenerateNewId();
        var masterList = new MasterList { Id = id };
        _repositoryMock.Setup(repo => repo.FindById(It.IsAny<ObjectId>(), It.IsAny<LocaleEnum>()))
            .ReturnsAsync(masterList);
        _repositoryMock.Setup(repo => repo.Delete(It.IsAny<ObjectId>()))
            .Returns(Task.CompletedTask);
        _unitServiceMock.Setup(c => c.CountUnitsByMasterListId(It.IsAny<ObjectId>(), It.IsAny<MasterListType>()))
            .ReturnsAsync(0);
        // Act
        await _service.DeleteMasterListById(id);

        // Assert
        _repositoryMock.Verify(repo => repo.Delete(id), Times.Once);
    }

    // [Test]
    // public async Task GetAllMasterList_ShouldReturnAllMasterLists()
    // {
    //     // Arrange
    //     var masterLists = new List<MasterList> { new MasterList(), new MasterList() };
    //     _repositoryMock.Setup(repo => repo.GetAll())
    //         .ReturnsAsync(masterLists);
    //
    //     // Act
    //     var result = await _service.GetAllMasterList();
    //
    //     // Assert
    //     Assert.That(masterLists, Is.EqualTo(result));
    // }

    /// <summary>
    /// Verifies that the service returns the master list when it is found by the specified id.
    /// </summary>
    [Test]
    public async Task GetMasterListById_ShouldReturnMasterList_WhenFound()
    {
        // Arrange
        var id = ObjectId.GenerateNewId();
        var masterList = new MasterList { Id = id };
        _repositoryMock.Setup(repo => repo.FindById(It.IsAny<ObjectId>(), It.IsAny<LocaleEnum>()))
            .ReturnsAsync(masterList);

        // Act
        var result = await _service.GetMasterListById(id, LocaleEnum.Default);

        // Assert
        Assert.That(masterList, Is.EqualTo(result));
    }

    /// <summary>
    /// Verifies that <c>GetMasterListByName</c> returns the matching <see cref="MasterList"/> when a master list with the specified name is found in the repository.
    /// </summary>
    [Test]
    public async Task GetMasterListByName_ShouldReturnMasterList_WhenFound()
    {
        // Arrange
        var name = "TestName";
        var masterList = new MasterList { Id = ObjectId.GenerateNewId(), Name = name };
        _repositoryMock.Setup(repo => repo.FindByName(It.IsAny<string>()))
            .ReturnsAsync(masterList);

        // Act
        var result = await _service.GetMasterListByName(name);

        // Assert
        Assert.That(masterList, Is.EqualTo(result));
    }

    /// <summary>
    /// Verifies that the InsertMasterList service method returns the same <see cref="MasterList"/> instance that was inserted, ensuring the service correctly retrieves the inserted entity by its identifier after persistence.
    /// </summary>
    [Test]
    public async Task InsertMasterList_ShouldReturnInsertedMasterList()
    {
        // Arrange
        var masterList = new MasterList { Id = ObjectId.GenerateNewId() };
        _repositoryMock.Setup(repo => repo.InsertOneAsync(It.IsAny<MasterList>()))
            .Returns(Task.CompletedTask);
        _repositoryMock.Setup(repo => repo.FindById(It.IsAny<ObjectId>()))
            .ReturnsAsync(masterList);

        // Act
        var result = await _service.InsertMasterList(masterList);

        // Assert
        Assert.That(masterList, Is.EqualTo(result));
    }

    /// <summary>
    /// Verifies that UpdateMasterList returns the updated master list when the repository successfully completes the update and finds the entity by id.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous test execution.</returns>
    [Test]
    public async Task UpdateMasterList_ShouldReturnUpdatedMasterList()
    {
        // Arrange
        var masterList = new MasterList { Id = ObjectId.GenerateNewId() };
        _repositoryMock.Setup(repo => repo.Update(It.IsAny<MasterList>()))
            .Returns(Task.CompletedTask);
        _repositoryMock.Setup(repo => repo.FindById(It.IsAny<ObjectId>(), It.IsAny<LocaleEnum>()))
            .ReturnsAsync(masterList);

        // Act
        var result = await _service.UpdateMasterList(masterList);

        // Assert
        Assert.That(masterList, Is.EqualTo(result));
    }
}