using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Services;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Moq;
using Xunit;

namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services;

public class OrderServiceTests
{
    private readonly Mock<IRepository<Basket>> _mockBasketRepo = new();
    private readonly Mock<IRepository<Order>> _mockOrderRepo = new();
    private readonly Mock<IRepository<CatalogItem>> _mockCatalogRepo = new();
    private readonly Mock<UriComposer> _uriComposer = new();
    private readonly Mock<IDateTime> _mockDateTimeService = new();

    public OrderServiceTests()
    {
        var buyerId = Guid.NewGuid().ToString();
        var catalogItem = 1;

        var basket = new Basket(buyerId);
        basket.AddItem(catalogItem, 10);
        
        _mockBasketRepo
            .Setup(x => x.GetByIdAsync(_basketId, It.IsAny<CancellationToken>()))
            .Returns(Task.FromResult(basket)!);

        var catalogItems = new List<CatalogItem>()
        {
            new(1, 1, "Red Stapler", "Stapler", 10, "/stapler.png")
            {
                Id = catalogItem
            }
        };
        
        _mockCatalogRepo.Setup(x => x.ListAsync(It.IsAny<CatalogItemsSpecification>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(catalogItems));
    }
    
    private readonly int _basketId = 1;
    
    [Fact]
    public async Task TestIsFlaky()
    {
        var obj = new OrderService(_mockBasketRepo.Object, _mockCatalogRepo.Object, _mockOrderRepo.Object, _uriComposer);

        var address = new Address("120 Freidrich Lane", "Austin", "TX", "US", "78744");
        var order = await obj.CreateOrderAsync(_basketId, address);
        
        Assert.Equal(address, order.ShipToAddress);
        //Assert.Equal(DateTime.UtcNow, order.OrderDate);
    }
    
    [Fact]
    public async Task TestWithInterface()
    {
        var utcNow = new DateTimeOffset(new DateTime(2022, 11, 28));
        _mockDateTimeService.Setup(x => x.UtcNow()).Returns(utcNow);
        
        var obj = new OrderServiceWithInterface(_mockBasketRepo.Object, _mockCatalogRepo.Object, _mockOrderRepo.Object, _uriComposer.Object, _mockDateTimeService.Object);

        var address = new Address("120 Freidrich Lane", "Austin", "TX", "US", "78744");
        var order = await obj.CreateOrderAsync(_basketId, address);
        
        Assert.Equal(utcNow, order.OrderDate);
    }
    
    [Fact]
    public async Task TestWithDelegate()
    {
        var utcNow = new DateTimeOffset(new DateTime(2022, 11, 28));
        
        var obj = new OrderServiceDelegate(_mockBasketRepo.Object, _mockCatalogRepo.Object, _mockOrderRepo.Object, 
            _uriComposer.Object, () => utcNow);

        var address = new Address("120 Freidrich Lane", "Austin", "TX", "US", "78744");
        var order = await obj.CreateOrderAsync(_basketId, address);
        
        Assert.Equal(utcNow, order.OrderDate);
    }
    
    [Fact]
    public async Task TestWithValue()
    {
        var utcNow = new DateTimeOffset(new DateTime(2022, 11, 28));
        var obj = new OrderServiceValue(_mockBasketRepo.Object, _mockCatalogRepo.Object, _mockOrderRepo.Object, _uriComposer);

        var address = new Address("120 Freidrich Lane", "Austin", "TX", "US", "78744");
        var order = await obj.CreateOrderAsync(_basketId, address, utcNow);
        
        Assert.Equal(utcNow, order.OrderDate);
    }
}
