using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Features.Catalog.Commands.CreateServiceCategory;
using Baya.Application.Features.Catalog.Commands.CreateServiceOptionGroup;
using Baya.Application.Features.Catalog.Commands.CreateServiceOptionValue;
using Baya.Application.Models.Catalog;
using Baya.Domain.Entities.Catalog;
using NSubstitute;

namespace Baya.Test.Foundation.Catalog;

public class CatalogAdminHandlerTests
{
    private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
    private readonly ICatalogRepository _catalog = Substitute.For<ICatalogRepository>();
    private readonly ICacheService _cache = Substitute.For<ICacheService>();

    public CatalogAdminHandlerTests()
    {
        _unitOfWork.CatalogRepository.Returns(_catalog);
    }

    [Fact]
    public async Task CreateCategory_Succeeds_AndInvalidatesCache()
    {
        var handler = new CreateServiceCategoryCommandHandler(_unitOfWork, _cache);

        var result = await handler.Handle(
            new CreateServiceCategoryCommand("مراقبت از سالمند", "Elderly Care", null, null, null, 1), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.Equal("Elderly Care", result.Result.NameEn);
        Assert.True(result.Result.IsActive);
        await _catalog.Received(1).AddCategoryAsync(Arg.Any<ServiceCategory>(), Arg.Any<CancellationToken>());
        await _unitOfWork.Received(1).CommitAsync();
        // Invalidation bumps the generation-token key so every cached catalog page refreshes.
        await _cache.Received().SetAsync("catalog:version", Arg.Any<string>(), Arg.Any<TimeSpan?>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task CreateOptionGroup_UnknownCategory_Fails()
    {
        _catalog.CategoryExistsAsync(99L, Arg.Any<CancellationToken>()).Returns(false);
        var handler = new CreateServiceOptionGroupCommandHandler(_unitOfWork, _cache);

        var result = await handler.Handle(
            new CreateServiceOptionGroupCommand(99L, "نوع شیفت", "Shift type", true, 1), CancellationToken.None);

        Assert.False(result.IsSuccess);
        await _catalog.DidNotReceive().AddGroupAsync(Arg.Any<ServiceOptionGroup>(), Arg.Any<CancellationToken>());
        await _unitOfWork.DidNotReceive().CommitAsync();
    }

    [Fact]
    public async Task CreateOptionGroup_CrossCategoryNull_Succeeds()
    {
        _catalog.GetGroupDtoAsync(Arg.Any<long>(), Arg.Any<CancellationToken>())
            .Returns(new OptionGroupDto(5, null, "نوع شیفت", "Shift type", true, 1, true, []));
        var handler = new CreateServiceOptionGroupCommandHandler(_unitOfWork, _cache);

        var result = await handler.Handle(
            new CreateServiceOptionGroupCommand(null, "نوع شیفت", "Shift type", true, 1), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.Null(result.Result.ServiceCategoryId);
        await _catalog.Received(1).AddGroupAsync(
            Arg.Is<ServiceOptionGroup>(g => g.ServiceCategoryId == null && g.IsRequired), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task CreateOptionValue_UnknownGroup_Fails()
    {
        _catalog.GroupExistsAsync(77L, Arg.Any<CancellationToken>()).Returns(false);
        var handler = new CreateServiceOptionValueCommandHandler(_unitOfWork, _cache);

        var result = await handler.Handle(
            new CreateServiceOptionValueCommand(77L, "شبانه‌روزی", "Live-in", 1), CancellationToken.None);

        Assert.False(result.IsSuccess);
        await _catalog.DidNotReceive().AddValueAsync(Arg.Any<ServiceOptionValue>(), Arg.Any<CancellationToken>());
    }
}
