using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.Goods;
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>
/// GoodsService 单元测试
/// </summary>
public class GoodsServiceTests : IDisposable
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly Mock<ILogger<GoodsService>> _mockLogger;
    private readonly GoodsService _goodsService;

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

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

        _goodsService = new GoodsService(_dbContext, _mockLogger.Object);
    }

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

    #region GetGoodsListAsync Tests

    [Fact]
    public async Task GetGoodsListAsync_WithNoFilters_ReturnsAllGoods()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var request = new GoodsListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _goodsService.GetGoodsListAsync(request);

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

    [Fact]
    public async Task GetGoodsListAsync_WithTitleFilter_ReturnsFilteredGoods()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var request = new GoodsListRequest { Title = "一番赏", Page = 1, PageSize = 10 };

        // Act
        var result = await _goodsService.GetGoodsListAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(1, result.Total);
        Assert.Contains(result.List, g => g.Title.Contains("一番赏"));
    }

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

        // Act
        var result = await _goodsService.GetGoodsListAsync(request);

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

    [Fact]
    public async Task GetGoodsListAsync_WithTypeFilter_ReturnsFilteredGoods()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var request = new GoodsListRequest { Type = 1, Page = 1, PageSize = 10 };

        // Act
        var result = await _goodsService.GetGoodsListAsync(request);

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

    [Fact]
    public async Task GetGoodsListAsync_WithPagination_ReturnsCorrectPage()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var request = new GoodsListRequest { Page = 1, PageSize = 2 };

        // Act
        var result = await _goodsService.GetGoodsListAsync(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);
    }

    [Fact]
    public async Task GetGoodsListAsync_ExcludesDeletedGoods()
    {
        // Arrange
        await SeedGoodsDataAsync();
        // Add a deleted goods
        _dbContext.Goods.Add(new Good
        {
            Title = "已删除商品",
            ImgUrl = "http://test.com/deleted.jpg",
            ImgUrlDetail = "http://test.com/deleted_detail.jpg",
            Price = 10,
            Type = 1,
            Status = 1,
            Stock = 10,
            DeletedAt = DateTime.Now,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        });
        await _dbContext.SaveChangesAsync();

        var request = new GoodsListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _goodsService.GetGoodsListAsync(request);

        // Assert
        Assert.Equal(3, result.Total); // Should not include deleted goods
    }

    #endregion

    #region GetGoodsDetailAsync Tests

    [Fact]
    public async Task GetGoodsDetailAsync_WithExistingGoods_ReturnsDetail()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync();

        // Act
        var result = await _goodsService.GetGoodsDetailAsync(goods.Id);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(goods.Id, result.Id);
        Assert.Equal(goods.Title, result.Title);
    }

    [Fact]
    public async Task GetGoodsDetailAsync_WithNonExistingGoods_ReturnsNull()
    {
        // Act
        var result = await _goodsService.GetGoodsDetailAsync(99999);

        // Assert
        Assert.Null(result);
    }

    [Fact]
    public async Task GetGoodsDetailAsync_WithDeletedGoods_ReturnsNull()
    {
        // Arrange
        var deletedGoods = new Good
        {
            Title = "已删除商品",
            ImgUrl = "http://test.com/deleted.jpg",
            ImgUrlDetail = "http://test.com/deleted_detail.jpg",
            Price = 10,
            Type = 1,
            Status = 1,
            Stock = 10,
            DeletedAt = DateTime.Now,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Goods.Add(deletedGoods);
        await _dbContext.SaveChangesAsync();

        // Act
        var result = await _goodsService.GetGoodsDetailAsync(deletedGoods.Id);

        // Assert
        Assert.Null(result);
    }

    #endregion

    #region CreateGoodsAsync Tests

    [Fact]
    public async Task CreateGoodsAsync_WithValidRequest_CreatesGoods()
    {
        // Arrange
        var request = new GoodsCreateRequest
        {
            Title = "新商品",
            Price = 100,
            Type = 1,
            ImgUrl = "http://test.com/new.jpg",
            ImgUrlDetail = "http://test.com/new_detail.jpg",
            Stock = 50,
            Sort = 1
        };

        // Act
        var goodsId = await _goodsService.CreateGoodsAsync(request, 1);

        // Assert
        Assert.True(goodsId > 0);
        var createdGoods = await _dbContext.Goods.FindAsync(goodsId);
        Assert.NotNull(createdGoods);
        Assert.Equal("新商品", createdGoods.Title);
        Assert.Equal(100, createdGoods.Price);
        Assert.Equal(0, createdGoods.Status); // Default to offline
    }

    [Fact]
    public async Task CreateGoodsAsync_WithInvalidType_ThrowsException()
    {
        // Arrange
        var request = new GoodsCreateRequest
        {
            Title = "新商品",
            Price = 100,
            Type = 999, // Invalid type
            ImgUrl = "http://test.com/new.jpg",
            ImgUrlDetail = "http://test.com/new_detail.jpg",
            Stock = 50
        };

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

    [Fact]
    public async Task CreateGoodsAsync_WithWelfareHouseType_SetsIsFlw()
    {
        // Arrange
        var request = new GoodsCreateRequest
        {
            Title = "福利屋商品",
            Price = 100,
            Type = 15, // 福利屋
            ImgUrl = "http://test.com/flw.jpg",
            ImgUrlDetail = "http://test.com/flw_detail.jpg",
            Stock = 50
        };

        // Act
        var goodsId = await _goodsService.CreateGoodsAsync(request, 1);

        // Assert
        var createdGoods = await _dbContext.Goods.FindAsync(goodsId);
        Assert.NotNull(createdGoods);
        Assert.Equal(1, createdGoods.IsFlw);
    }

    #endregion

    #region UpdateGoodsAsync Tests

    [Fact]
    public async Task UpdateGoodsAsync_WithValidRequest_UpdatesGoods()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var request = new GoodsUpdateRequest
        {
            Title = "更新后的标题",
            Price = 200,
            Type = goods.Type,
            ImgUrl = goods.ImgUrl,
            ImgUrlDetail = goods.ImgUrlDetail,
            Stock = goods.Stock + 10 // Increase stock
        };

        // Act
        var result = await _goodsService.UpdateGoodsAsync(goods.Id, request, 1);

        // Assert
        Assert.True(result);
        var updatedGoods = await _dbContext.Goods.FindAsync(goods.Id);
        Assert.Equal("更新后的标题", updatedGoods!.Title);
        Assert.Equal(200, updatedGoods.Price);
    }

    [Fact]
    public async Task UpdateGoodsAsync_WithNonExistingGoods_ThrowsException()
    {
        // Arrange
        var request = new GoodsUpdateRequest
        {
            Title = "更新",
            Price = 100,
            Type = 1,
            ImgUrl = "http://test.com/test.jpg",
            ImgUrlDetail = "http://test.com/test_detail.jpg",
            Stock = 10
        };

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

    [Fact]
    public async Task UpdateGoodsAsync_WithReducedStock_ThrowsException()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var request = new GoodsUpdateRequest
        {
            Title = goods.Title,
            Price = goods.Price,
            Type = goods.Type,
            ImgUrl = goods.ImgUrl,
            ImgUrlDetail = goods.ImgUrlDetail,
            Stock = goods.Stock - 1 // Reduce stock
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _goodsService.UpdateGoodsAsync(goods.Id, request, 1));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, exception.Code);
        Assert.Contains("库存", exception.Message);
    }

    #endregion

    #region DeleteGoodsAsync Tests

    [Fact]
    public async Task DeleteGoodsAsync_WithExistingGoods_SoftDeletes()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync();

        // Act
        var result = await _goodsService.DeleteGoodsAsync(goods.Id, 1);

        // Assert
        Assert.True(result);
        var deletedGoods = await _dbContext.Goods.FindAsync(goods.Id);
        Assert.NotNull(deletedGoods!.DeletedAt);
    }

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

    #endregion

    #region SetGoodsStatusAsync Tests

    [Fact]
    public async Task SetGoodsStatusAsync_WithValidGoods_UpdatesStatus()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync(g => g.Status == 0);

        // Act
        var result = await _goodsService.SetGoodsStatusAsync(goods.Id, 1, 1);

        // Assert
        Assert.True(result);
        var updatedGoods = await _dbContext.Goods.FindAsync(goods.Id);
        Assert.Equal(1, updatedGoods!.Status);
    }

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

    #endregion


    #region Prize Management Tests

    [Fact]
    public async Task GetPrizesAsync_WithExistingGoods_ReturnsPrizes()
    {
        // Arrange
        await SeedGoodsWithPrizesAsync();
        var goods = await _dbContext.Goods.FirstAsync();

        // Act
        var result = await _goodsService.GetPrizesAsync(goods.Id);

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

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

    [Fact]
    public async Task AddPrizeAsync_WithValidRequest_CreatesPrize()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var request = new PrizeCreateRequest
        {
            Title = "新奖品",
            ImgUrl = "http://test.com/prize.jpg",
            Stock = 10,
            Price = 50,
            Money = 30,
            ScMoney = 25,
            RealPro = 10,
            GoodsType = 1
        };

        // Act
        var prizeId = await _goodsService.AddPrizeAsync(goods.Id, request);

        // Assert
        Assert.True(prizeId > 0);
        var createdPrize = await _dbContext.GoodsItems.FindAsync(prizeId);
        Assert.NotNull(createdPrize);
        Assert.Equal("新奖品", createdPrize.Title);
        Assert.NotNull(createdPrize.PrizeCode);
    }

    [Fact]
    public async Task AddPrizeAsync_GeneratesUniquePrizeCode()
    {
        // Arrange
        await SeedGoodsDataAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var request = new PrizeCreateRequest
        {
            Title = "奖品",
            ImgUrl = "http://test.com/prize.jpg",
            Stock = 10,
            Price = 50,
            Money = 30,
            ScMoney = 25,
            RealPro = 10,
            GoodsType = 1
        };

        // Act
        var prizeId1 = await _goodsService.AddPrizeAsync(goods.Id, request);
        var prizeId2 = await _goodsService.AddPrizeAsync(goods.Id, request);

        // Assert
        var prize1 = await _dbContext.GoodsItems.FindAsync(prizeId1);
        var prize2 = await _dbContext.GoodsItems.FindAsync(prizeId2);
        Assert.NotEqual(prize1!.PrizeCode, prize2!.PrizeCode);
    }

    [Fact]
    public async Task UpdatePrizeAsync_WithValidRequest_UpdatesPrize()
    {
        // Arrange
        await SeedGoodsWithPrizesAsync();
        var prize = await _dbContext.GoodsItems.FirstAsync();
        var request = new PrizeUpdateRequest
        {
            Title = "更新后的奖品",
            ImgUrl = prize.ImgUrl,
            Stock = 20,
            Price = 100,
            Money = 60,
            ScMoney = 50,
            RealPro = 15,
            GoodsType = 1
        };

        // Act
        var result = await _goodsService.UpdatePrizeAsync(prize.Id, request);

        // Assert
        Assert.True(result);
        var updatedPrize = await _dbContext.GoodsItems.FindAsync(prize.Id);
        Assert.Equal("更新后的奖品", updatedPrize!.Title);
        Assert.Equal(100, updatedPrize.Price);
    }

    [Fact]
    public async Task UpdatePrizeAsync_WithNonExistingPrize_ThrowsException()
    {
        // Arrange
        var request = new PrizeUpdateRequest
        {
            Title = "更新",
            ImgUrl = "http://test.com/prize.jpg",
            Stock = 10,
            Price = 50,
            Money = 30,
            ScMoney = 25,
            RealPro = 10,
            GoodsType = 1
        };

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

    [Fact]
    public async Task DeletePrizeAsync_WithExistingPrize_DeletesPrize()
    {
        // Arrange
        await SeedGoodsWithPrizesAsync();
        var prize = await _dbContext.GoodsItems.FirstAsync();
        var goodsId = prize.GoodsId;
        var initialCount = await _dbContext.GoodsItems.CountAsync(gi => gi.GoodsId == goodsId);

        // Act
        var result = await _goodsService.DeletePrizeAsync(prize.Id);

        // Assert
        Assert.True(result);
        var deletedPrize = await _dbContext.GoodsItems.FindAsync(prize.Id);
        Assert.Null(deletedPrize);
        var newCount = await _dbContext.GoodsItems.CountAsync(gi => gi.GoodsId == goodsId);
        Assert.Equal(initialCount - 1, newCount);
    }

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

    #endregion

    #region GetGoodsTypesAsync Tests

    [Fact]
    public async Task GetGoodsTypesAsync_ReturnsAllTypes()
    {
        // Arrange
        await SeedGoodsTypesAsync();

        // Act
        var result = await _goodsService.GetGoodsTypesAsync();

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

    #endregion

    #region Helper Methods

    private async Task SeedGoodsDataAsync()
    {
        var goods = new List<Good>
        {
            new()
            {
                Title = "一番赏测试商品",
                ImgUrl = "http://test.com/1.jpg",
                ImgUrlDetail = "http://test.com/1_detail.jpg",
                Price = 100,
                Type = 1,
                Status = 1,
                Stock = 100,
                SaleStock = 10,
                Sort = 1,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Title = "无限赏测试商品",
                ImgUrl = "http://test.com/2.jpg",
                ImgUrlDetail = "http://test.com/2_detail.jpg",
                Price = 50,
                Type = 2,
                Status = 1,
                Stock = 200,
                SaleStock = 50,
                Sort = 2,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Title = "盲盒测试商品",
                ImgUrl = "http://test.com/3.jpg",
                ImgUrlDetail = "http://test.com/3_detail.jpg",
                Price = 30,
                Type = 8,
                Status = 0,
                Stock = 50,
                SaleStock = 0,
                Sort = 3,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.Goods.AddRange(goods);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedGoodsWithPrizesAsync()
    {
        var goods = new Good
        {
            Title = "带奖品的商品",
            ImgUrl = "http://test.com/goods.jpg",
            ImgUrlDetail = "http://test.com/goods_detail.jpg",
            Price = 100,
            Type = 1,
            Status = 1,
            Stock = 100,
            PrizeNum = 2,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Goods.Add(goods);
        await _dbContext.SaveChangesAsync();

        var prizes = new List<GoodsItem>
        {
            new()
            {
                GoodsId = goods.Id,
                Num = 1,
                Title = "A赏",
                ImgUrl = "http://test.com/prize1.jpg",
                Stock = 1,
                SurplusStock = 1,
                Price = 500,
                Money = 300,
                ScMoney = 250,
                RealPro = 1,
                GoodsType = 1,
                Sort = 1,
                PrizeCode = "PC001",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                GoodsId = goods.Id,
                Num = 2,
                Title = "B赏",
                ImgUrl = "http://test.com/prize2.jpg",
                Stock = 5,
                SurplusStock = 5,
                Price = 200,
                Money = 100,
                ScMoney = 80,
                RealPro = 5,
                GoodsType = 1,
                Sort = 2,
                PrizeCode = "PC002",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        _dbContext.GoodsItems.AddRange(prizes);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedGoodsTypesAsync()
    {
        var types = new List<GoodsType>
        {
            new() { Name = "一番赏", Value = 1, SortOrder = 1, IsShow = 1, FlName = "一番赏" },
            new() { Name = "无限赏", Value = 2, SortOrder = 2, IsShow = 1, FlName = "无限赏" },
            new() { Name = "盲盒", Value = 8, SortOrder = 3, IsShow = 1, FlName = "盲盒" }
        };

        _dbContext.GoodsTypes.AddRange(types);
        await _dbContext.SaveChangesAsync();
    }

    #endregion
}
