using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.Order;
using HoneyBox.Admin.Business.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace HoneyBox.Tests.Services;

/// <summary>
/// OrderService 单元测试
/// </summary>
public class OrderServiceTests : IDisposable
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly Mock<ILogger<OrderService>> _mockLogger;
    private readonly OrderService _orderService;

    public OrderServiceTests()
    {
        // 使用 InMemory 数据库
        var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        _dbContext = new HoneyBoxDbContext(options);
        _mockLogger = new Mock<ILogger<OrderService>>();

        _orderService = new OrderService(_dbContext, _mockLogger.Object);
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }

    #region GetOrderListAsync Tests

    [Fact]
    public async Task GetOrderListAsync_WithNoFilters_ReturnsAllOrders()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetOrderListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(3, result.Total);
        Assert.Equal(3, result.List.Count);
    }

    [Fact]
    public async Task GetOrderListAsync_WithUserIdFilter_ReturnsFilteredOrders()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderListRequest { UserId = 1, Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetOrderListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.All(result.List, o => Assert.Equal(1, o.UserId));
    }

    [Fact]
    public async Task GetOrderListAsync_WithOrderNumFilter_ReturnsFilteredOrders()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderListRequest { OrderNum = "ORD001", Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetOrderListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Single(result.List);
        Assert.Contains("ORD001", result.List[0].OrderNum);
    }

    [Fact]
    public async Task GetOrderListAsync_WithStatusFilter_ReturnsFilteredOrders()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderListRequest { Status = 1, Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetOrderListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.All(result.List, o => Assert.Equal(1, o.Status));
    }

    [Fact]
    public async Task GetOrderListAsync_WithDateRangeFilter_ReturnsFilteredOrders()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderListRequest 
        { 
            StartDate = DateTime.Today.AddDays(-1),
            EndDate = DateTime.Today.AddDays(1),
            Page = 1, 
            PageSize = 10 
        };

        // Act
        var result = await _orderService.GetOrderListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.True(result.Total > 0);
    }

    [Fact]
    public async Task GetOrderListAsync_WithPagination_ReturnsCorrectPage()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderListRequest { Page = 1, PageSize = 2 };

        // Act
        var result = await _orderService.GetOrderListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(3, result.Total);
        Assert.Equal(2, result.List.Count);
        Assert.Equal(1, result.Page);
        Assert.Equal(2, result.PageSize);
    }

    #endregion

    #region GetOrderDetailAsync Tests

    [Fact]
    public async Task GetOrderDetailAsync_WithExistingOrder_ReturnsDetail()
    {
        // Arrange
        await SeedOrderWithItemsAsync();
        var order = await _dbContext.Orders.FirstAsync();

        // Act
        var result = await _orderService.GetOrderDetailAsync(order.Id);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(order.Id, result.Id);
        Assert.Equal(order.OrderNum, result.OrderNum);
    }

    [Fact]
    public async Task GetOrderDetailAsync_WithNonExistingOrder_ReturnsNull()
    {
        // Act
        var result = await _orderService.GetOrderDetailAsync(99999);

        // Assert
        Assert.Null(result);
    }

    [Fact]
    public async Task GetOrderDetailAsync_GroupsPrizesByPrizeCode()
    {
        // Arrange
        await SeedOrderWithItemsAsync();
        var order = await _dbContext.Orders.FirstAsync();

        // Act
        var result = await _orderService.GetOrderDetailAsync(order.Id);

        // Assert
        Assert.NotNull(result);
        Assert.NotEmpty(result.PrizeGroups);
        // Verify grouping
        foreach (var group in result.PrizeGroups)
        {
            Assert.True(group.Count > 0);
            Assert.NotEmpty(group.Items);
        }
    }

    #endregion


    #region GetStuckOrdersAsync Tests

    [Fact]
    public async Task GetStuckOrdersAsync_ReturnsOrdersWithPendingItems()
    {
        // Arrange
        await SeedOrderWithItemsAsync();
        var request = new OrderListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetStuckOrdersAsync(request);

        // Assert
        Assert.NotNull(result);
        // All returned orders should have pending items
        foreach (var order in result.List)
        {
            var hasPendingItems = await _dbContext.OrderItems
                .AnyAsync(oi => oi.OrderId == order.Id && oi.Status == 0);
            Assert.True(hasPendingItems);
        }
    }

    #endregion

    #region GetShippingOrdersAsync Tests

    [Fact]
    public async Task GetShippingOrdersAsync_WithNoFilters_ReturnsAllShippingOrders()
    {
        // Arrange
        await SeedShippingOrderDataAsync();
        var request = new ShippingOrderListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetShippingOrdersAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(2, result.Total);
    }

    [Fact]
    public async Task GetShippingOrdersAsync_WithStatusFilter_ReturnsFilteredOrders()
    {
        // Arrange
        await SeedShippingOrderDataAsync();
        var request = new ShippingOrderListRequest { Status = 1, Page = 1, PageSize = 10 };

        // Act
        var result = await _orderService.GetShippingOrdersAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.All(result.List, s => Assert.Equal(1, s.Status));
    }

    #endregion

    #region ShipOrderAsync Tests

    [Fact]
    public async Task ShipOrderAsync_WithValidOrder_UpdatesStatus()
    {
        // Arrange
        await SeedShippingOrderDataAsync();
        var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 1);
        var request = new ShipOrderRequest
        {
            CourierName = "顺丰速运",
            CourierNumber = "SF123456789"
        };

        // Act
        var result = await _orderService.ShipOrderAsync(shippingOrder.Id, request, 1);

        // Assert
        Assert.True(result);
        var updatedOrder = await _dbContext.OrderItemsSends.FindAsync(shippingOrder.Id);
        Assert.Equal(2, updatedOrder!.Status); // 已发货
        Assert.Equal("顺丰速运", updatedOrder.CourierName);
        Assert.Equal("SF123456789", updatedOrder.CourierNumber);
    }

    [Fact]
    public async Task ShipOrderAsync_WithNonExistingOrder_ThrowsException()
    {
        // Arrange
        var request = new ShipOrderRequest
        {
            CourierName = "顺丰速运",
            CourierNumber = "SF123456789"
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _orderService.ShipOrderAsync(99999, request, 1));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
    }

    [Fact]
    public async Task ShipOrderAsync_WithInvalidStatus_ThrowsException()
    {
        // Arrange
        await SeedShippingOrderDataAsync();
        var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 2); // 已发货
        var request = new ShipOrderRequest
        {
            CourierName = "顺丰速运",
            CourierNumber = "SF123456789"
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _orderService.ShipOrderAsync(shippingOrder.Id, request, 1));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, exception.Code);
    }

    #endregion

    #region CancelShippingOrderAsync Tests

    [Fact]
    public async Task CancelShippingOrderAsync_WithValidOrder_RestoresPrizes()
    {
        // Arrange
        await SeedShippingOrderWithItemsAsync();
        var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 1);
        var itemsCount = await _dbContext.OrderItems.CountAsync(oi => oi.SendNum == shippingOrder.SendNum);

        // Act
        var result = await _orderService.CancelShippingOrderAsync(shippingOrder.Id, 1);

        // Assert
        Assert.True(result);
        var updatedOrder = await _dbContext.OrderItemsSends.FindAsync(shippingOrder.Id);
        Assert.Equal(4, updatedOrder!.Status); // 已取消

        // Verify items are restored
        var restoredItems = await _dbContext.OrderItems
            .Where(oi => oi.SendNum == null && oi.Status == 0)
            .CountAsync();
        Assert.True(restoredItems >= itemsCount);
    }

    [Fact]
    public async Task CancelShippingOrderAsync_WithNonExistingOrder_ThrowsException()
    {
        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _orderService.CancelShippingOrderAsync(99999, 1));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
    }

    #endregion

    #region ExportOrdersAsync Tests

    [Fact]
    public async Task ExportOrdersAsync_ReturnsValidCsvBytes()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderExportRequest();

        // Act
        var result = await _orderService.ExportOrdersAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.True(result.Length > 0);
        // Verify it's valid UTF-8 with BOM
        Assert.Equal(0xEF, result[0]);
        Assert.Equal(0xBB, result[1]);
        Assert.Equal(0xBF, result[2]);
    }

    [Fact]
    public async Task ExportOrdersAsync_WithFilters_ExportsFilteredData()
    {
        // Arrange
        await SeedOrderDataAsync();
        var request = new OrderExportRequest { Status = 1 };

        // Act
        var result = await _orderService.ExportOrdersAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.True(result.Length > 0);
    }

    #endregion

    #region Helper Methods

    private async Task SeedUserDataAsync()
    {
        var users = new List<User>
        {
            new()
            {
                Id = 1,
                Uid = "U001",
                Nickname = "测试用户1",
                Mobile = "13800138001",
                OpenId = "openid1",
                HeadImg = "http://test.com/head1.jpg",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Id = 2,
                Uid = "U002",
                Nickname = "测试用户2",
                Mobile = "13800138002",
                OpenId = "openid2",
                HeadImg = "http://test.com/head2.jpg",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.Users.AddRange(users);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedOrderDataAsync()
    {
        await SeedUserDataAsync();

        var orders = new List<Order>
        {
            new()
            {
                UserId = 1,
                OrderNum = "ORD001",
                OrderTotal = 100,
                OrderZheTotal = 90,
                Price = 90,
                UseMoney = 0,
                UseIntegral = 0,
                UseScore = 0,
                Zhe = 0.9m,
                GoodsId = 1,
                Num = 1,
                GoodsPrice = 100,
                GoodsTitle = "测试商品1",
                PrizeNum = 1,
                Status = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PayTime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PayType = 1,
                OrderType = 0,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                UserId = 1,
                OrderNum = "ORD002",
                OrderTotal = 200,
                OrderZheTotal = 180,
                Price = 180,
                UseMoney = 0,
                UseIntegral = 0,
                UseScore = 0,
                Zhe = 0.9m,
                GoodsId = 2,
                Num = 2,
                GoodsPrice = 100,
                GoodsTitle = "测试商品2",
                PrizeNum = 2,
                Status = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PayTime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PayType = 1,
                OrderType = 0,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                UserId = 2,
                OrderNum = "ORD003",
                OrderTotal = 50,
                OrderZheTotal = 50,
                Price = 50,
                UseMoney = 0,
                UseIntegral = 0,
                UseScore = 0,
                Zhe = 1,
                GoodsId = 1,
                Num = 1,
                GoodsPrice = 50,
                GoodsTitle = "测试商品3",
                PrizeNum = 1,
                Status = 0,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PayTime = 0,
                PayType = 0,
                OrderType = 0,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.Orders.AddRange(orders);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedOrderWithItemsAsync()
    {
        await SeedOrderDataAsync();

        var order = await _dbContext.Orders.FirstAsync();
        var orderItems = new List<OrderItem>
        {
            new()
            {
                OrderId = order.Id,
                UserId = order.UserId,
                Status = 0, // 待处理
                GoodsId = order.GoodsId,
                Num = 1,
                ShangId = 1,
                GoodslistId = 1,
                GoodslistTitle = "A赏",
                GoodslistImgurl = "http://test.com/prize1.jpg",
                GoodslistPrice = 500,
                GoodslistMoney = 300,
                GoodslistType = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PrizeCode = "PC001",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                OrderId = order.Id,
                UserId = order.UserId,
                Status = 0, // 待处理
                GoodsId = order.GoodsId,
                Num = 2,
                ShangId = 2,
                GoodslistId = 2,
                GoodslistTitle = "B赏",
                GoodslistImgurl = "http://test.com/prize2.jpg",
                GoodslistPrice = 200,
                GoodslistMoney = 100,
                GoodslistType = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PrizeCode = "PC002",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                OrderId = order.Id,
                UserId = order.UserId,
                Status = 0, // 待处理
                GoodsId = order.GoodsId,
                Num = 3,
                ShangId = 2,
                GoodslistId = 2,
                GoodslistTitle = "B赏",
                GoodslistImgurl = "http://test.com/prize2.jpg",
                GoodslistPrice = 200,
                GoodslistMoney = 100,
                GoodslistType = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PrizeCode = "PC002", // Same prize code for grouping test
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.OrderItems.AddRange(orderItems);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedShippingOrderDataAsync()
    {
        await SeedUserDataAsync();

        var shippingOrders = new List<OrderItemsSend>
        {
            new()
            {
                UserId = 1,
                SendNum = "SEND001",
                Freight = 10,
                Status = 1, // 待发货
                Count = 2,
                Name = "张三",
                Mobile = "13800138001",
                Address = "北京市朝阳区xxx",
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                UserId = 2,
                SendNum = "SEND002",
                Freight = 15,
                Status = 2, // 已发货
                Count = 1,
                Name = "李四",
                Mobile = "13800138002",
                Address = "上海市浦东新区xxx",
                CourierName = "顺丰速运",
                CourierNumber = "SF987654321",
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                SendTime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.OrderItemsSends.AddRange(shippingOrders);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedShippingOrderWithItemsAsync()
    {
        await SeedShippingOrderDataAsync();

        var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 1);
        var orderItems = new List<OrderItem>
        {
            new()
            {
                OrderId = 1,
                UserId = shippingOrder.UserId,
                SendNum = shippingOrder.SendNum,
                Status = 2, // 已发货状态
                GoodsId = 1,
                Num = 1,
                ShangId = 1,
                GoodslistId = 1,
                GoodslistTitle = "A赏",
                GoodslistImgurl = "http://test.com/prize1.jpg",
                GoodslistPrice = 500,
                GoodslistMoney = 300,
                GoodslistType = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PrizeCode = "PC001",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                OrderId = 1,
                UserId = shippingOrder.UserId,
                SendNum = shippingOrder.SendNum,
                Status = 2, // 已发货状态
                GoodsId = 1,
                Num = 2,
                ShangId = 2,
                GoodslistId = 2,
                GoodslistTitle = "B赏",
                GoodslistImgurl = "http://test.com/prize2.jpg",
                GoodslistPrice = 200,
                GoodslistMoney = 100,
                GoodslistType = 1,
                Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
                PrizeCode = "PC002",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.OrderItems.AddRange(orderItems);
        await _dbContext.SaveChangesAsync();
    }

    #endregion
}
