using HoneyBox.Core.Interfaces;
using HoneyBox.Core.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Lottery;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace HoneyBox.Tests.Integration;

/// <summary>
/// 指定用户中奖功能端到端集成测试
/// 测试完整抽奖流程：配置指定中奖 → 用户抽奖 → 验证结果
/// Requirements: 2.3, 2.4, 2.5, 3.3, 3.4, 3.5
/// </summary>
public class DesignatedPrizeLotteryIntegrationTests : IDisposable
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly Mock<IInventoryManager> _mockInventoryManager;
    private readonly Mock<IRewardService> _mockRewardService;
    private readonly Mock<ILogger<LotteryEngine>> _mockLogger;
    private readonly LotteryEngine _lotteryEngine;

    public DesignatedPrizeLotteryIntegrationTests()
    {
        var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning))
            .Options;

        _dbContext = new HoneyBoxDbContext(options);
        _mockInventoryManager = new Mock<IInventoryManager>();
        _mockRewardService = new Mock<IRewardService>();
        _mockLogger = new Mock<ILogger<LotteryEngine>>();
        _lotteryEngine = new LotteryEngine(_dbContext, _mockInventoryManager.Object, _mockRewardService.Object, _mockLogger.Object);
    }

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

    #region Test Data Setup

    private async Task<User> CreateTestUserAsync(int id, string nickname = "测试用户")
    {
        var user = new User
        {
            Id = id,
            OpenId = $"test_openid_{id}",
            Uid = $"test_uid_{id}",
            Nickname = nickname,
            HeadImg = "avatar.jpg",
            Mobile = $"1380013800{id}",
            Money = 1000,
            Integral = 10000,
            Money2 = 5000,
            IsTest = 0,
            Status = 1,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Users.Add(user);
        await _dbContext.SaveChangesAsync();
        return user;
    }

    private async Task<Good> CreateTestGoodsAsync(int id, byte type = 1, int status = 1)
    {
        var goods = new Good
        {
            Id = id,
            Title = $"测试商品{id}",
            Type = type,
            Status = (byte)status,
            ShowIs = 0,
            Price = 10,
            Stock = 100,
            SaleStock = 0,
            LockIs = 0,
            IsShouZhe = 0,
            QuanjuXiangou = 0,
            DailyXiangou = 0,
            ChoujiangXianzhi = 0,
            ImgUrl = "img.jpg",
            ImgUrlDetail = "detail.jpg",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Goods.Add(goods);
        await _dbContext.SaveChangesAsync();
        return goods;
    }

    private async Task<List<GoodsItem>> CreateTestGoodsItemsAsync(int goodsId, int count = 5)
    {
        var items = new List<GoodsItem>();
        for (int i = 1; i <= count; i++)
        {
            var item = new GoodsItem
            {
                Id = goodsId * 100 + i,
                GoodsId = goodsId,
                Num = 1,
                Title = $"奖品{i}",
                Stock = 10,
                SurplusStock = 10,
                Price = 100 * i,
                Money = 50 * i,
                ScMoney = 40 * i,
                ShangId = i,
                GoodsListId = 0,
                ImgUrl = $"prize{i}.jpg",
                Sort = i,
                RealPro = 20, // 每个奖品20%概率
                GoodsType = 1,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            };
            items.Add(item);
        }
        _dbContext.GoodsItems.AddRange(items);
        await _dbContext.SaveChangesAsync();
        return items;
    }

    private async Task<GoodsDesignatedPrize> CreateDesignatedPrizeConfigAsync(int goodsId, int goodsItemId, int userId, bool isActive = true)
    {
        var config = new GoodsDesignatedPrize
        {
            GoodsId = goodsId,
            GoodsItemId = goodsItemId,
            UserId = userId,
            IsActive = isActive,
            Remark = "测试指定中奖配置",
            CreatedAt = DateTime.Now
        };
        _dbContext.GoodsDesignatedPrizes.Add(config);
        await _dbContext.SaveChangesAsync();
        return config;
    }

    private List<PrizeProbability> CreatePrizeProbabilities(List<GoodsItem> items)
    {
        var totalStock = items.Sum(i => i.SurplusStock);
        return items.Select(i => new PrizeProbability
        {
            GoodsItemId = i.Id,
            Title = i.Title,
            ImgUrl = i.ImgUrl,
            ShangId = i.ShangId ?? 0,
            SurplusStock = i.SurplusStock,
            Probability = (decimal)i.SurplusStock / totalStock * 100,
            Weight = i.SurplusStock,
            Price = i.Price,
            ScMoney = i.ScMoney,
            GoodsType = i.GoodsType,
            Doubling = i.Doubling,
            IsLingzhu = i.IsLingzhu,
            ParentGoodsListId = i.GoodsListId
        }).ToList();
    }

    private List<PrizeProbability> CreatePrizeProbabilitiesByRealPro(List<GoodsItem> items)
    {
        var totalPro = items.Sum(i => i.RealPro);
        return items.Select(i => new PrizeProbability
        {
            GoodsItemId = i.Id,
            Title = i.Title,
            ImgUrl = i.ImgUrl,
            ShangId = i.ShangId ?? 0,
            SurplusStock = i.SurplusStock,
            Probability = i.RealPro,
            Weight = (double)i.RealPro,
            Price = i.Price,
            ScMoney = i.ScMoney,
            GoodsType = i.GoodsType,
            Doubling = i.Doubling,
            IsLingzhu = i.IsLingzhu,
            ParentGoodsListId = i.GoodsListId
        }).ToList();
    }

    #endregion

    #region 有限赏完整流程测试 (Requirements 2.3, 2.4, 2.5)

    /// <summary>
    /// 有限赏完整流程：配置指定中奖 → 指定用户抽奖 → 验证指定用户可以抽到指定奖品
    /// Requirements: 2.3
    /// </summary>
    [Fact]
    public async Task FiniteLottery_DesignatedUser_CanDrawDesignatedPrize()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var goods = await CreateTestGoodsAsync(1, type: 1); // 有限赏
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);
        var designatedItem = items[0]; // 第一个奖品指定给用户1
        await CreateDesignatedPrizeConfigAsync(goods.Id, designatedItem.Id, designatedUser.Id);

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, designatedUser.Id, designatedPrizes);

        // Assert
        Assert.Contains(filteredPool, p => p.GoodsItemId == designatedItem.Id);
        Assert.Equal(5, filteredPool.Count); // 指定用户可以看到所有奖品（普通+自己的指定）
    }

    /// <summary>
    /// 有限赏完整流程：配置指定中奖 → 普通用户抽奖 → 验证普通用户无法抽到指定奖品
    /// Requirements: 2.4
    /// </summary>
    [Fact]
    public async Task FiniteLottery_NonDesignatedUser_CannotDrawDesignatedPrize()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 1);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);
        var designatedItem = items[0];
        await CreateDesignatedPrizeConfigAsync(goods.Id, designatedItem.Id, designatedUser.Id);

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, normalUser.Id, designatedPrizes);

        // Assert
        Assert.DoesNotContain(filteredPool, p => p.GoodsItemId == designatedItem.Id);
        Assert.Equal(4, filteredPool.Count); // 普通用户只能看到4个普通奖品
    }

    /// <summary>
    /// 有限赏兜底机制测试：只剩指定奖品时普通用户可以抽奖
    /// Requirements: 2.5
    /// </summary>
    [Fact]
    public async Task FiniteLottery_FallbackMechanism_WhenOnlyDesignatedPrizesRemain()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 1);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 3);

        // 所有奖品都指定给用户1
        foreach (var item in items)
        {
            await CreateDesignatedPrizeConfigAsync(goods.Id, item.Id, designatedUser.Id);
        }

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, normalUser.Id, designatedPrizes);

        // Assert - 兜底机制触发，普通用户可以抽到所有奖品
        Assert.Equal(3, filteredPool.Count);
        Assert.Equal(prizePool.Count, filteredPool.Count);
    }

    /// <summary>
    /// 有限赏：多个指定奖品配置测试
    /// Requirements: 2.3, 2.4
    /// </summary>
    [Fact]
    public async Task FiniteLottery_MultipleDesignatedPrizes_FilteredCorrectly()
    {
        // Arrange
        var user1 = await CreateTestUserAsync(1, "用户1");
        var user2 = await CreateTestUserAsync(2, "用户2");
        var user3 = await CreateTestUserAsync(3, "用户3");
        var goods = await CreateTestGoodsAsync(1, type: 1);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);

        // 奖品1指定给用户1，奖品2指定给用户2
        await CreateDesignatedPrizeConfigAsync(goods.Id, items[0].Id, user1.Id);
        await CreateDesignatedPrizeConfigAsync(goods.Id, items[1].Id, user2.Id);

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act - 用户1的奖品池
        var user1Pool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, user1.Id, designatedPrizes);
        // Act - 用户2的奖品池
        var user2Pool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, user2.Id, designatedPrizes);
        // Act - 用户3的奖品池（普通用户）
        var user3Pool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, user3.Id, designatedPrizes);

        // Assert
        Assert.Equal(4, user1Pool.Count); // 3个普通 + 1个自己的指定
        Assert.Contains(user1Pool, p => p.GoodsItemId == items[0].Id);
        Assert.DoesNotContain(user1Pool, p => p.GoodsItemId == items[1].Id);

        Assert.Equal(4, user2Pool.Count); // 3个普通 + 1个自己的指定
        Assert.Contains(user2Pool, p => p.GoodsItemId == items[1].Id);
        Assert.DoesNotContain(user2Pool, p => p.GoodsItemId == items[0].Id);

        Assert.Equal(3, user3Pool.Count); // 只有3个普通奖品
        Assert.DoesNotContain(user3Pool, p => p.GoodsItemId == items[0].Id);
        Assert.DoesNotContain(user3Pool, p => p.GoodsItemId == items[1].Id);
    }

    #endregion

    #region 无限赏完整流程测试 (Requirements 3.3, 3.4, 3.5)

    /// <summary>
    /// 无限赏完整流程：配置指定中奖 → 指定用户抽奖 → 验证指定用户可以抽到指定奖品
    /// Requirements: 3.4
    /// </summary>
    [Fact]
    public async Task InfiniteLottery_DesignatedUser_CanDrawDesignatedPrize()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var goods = await CreateTestGoodsAsync(1, type: 2); // 无限赏
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);
        var designatedItem = items[0];
        await CreateDesignatedPrizeConfigAsync(goods.Id, designatedItem.Id, designatedUser.Id);

        var prizePool = CreatePrizeProbabilitiesByRealPro(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolStrict(prizePool, designatedUser.Id, designatedPrizes);

        // Assert
        Assert.Contains(filteredPool, p => p.GoodsItemId == designatedItem.Id);
        Assert.Equal(5, filteredPool.Count);
    }

    /// <summary>
    /// 无限赏完整流程：配置指定中奖 → 普通用户抽奖 → 验证普通用户永远无法抽到指定奖品
    /// Requirements: 3.3, 3.5
    /// </summary>
    [Fact]
    public async Task InfiniteLottery_NonDesignatedUser_CanNeverDrawDesignatedPrize()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 2);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);
        var designatedItem = items[0];
        await CreateDesignatedPrizeConfigAsync(goods.Id, designatedItem.Id, designatedUser.Id);

        var prizePool = CreatePrizeProbabilitiesByRealPro(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolStrict(prizePool, normalUser.Id, designatedPrizes);

        // Assert - 严格模式，普通用户永远看不到指定奖品
        Assert.DoesNotContain(filteredPool, p => p.GoodsItemId == designatedItem.Id);
        Assert.Equal(4, filteredPool.Count);
    }

    /// <summary>
    /// 无限赏严格模式：所有奖品都指定给其他用户时，普通用户奖品池为空（无兜底）
    /// Requirements: 3.5
    /// </summary>
    [Fact]
    public async Task InfiniteLottery_StrictMode_NoFallback_WhenAllPrizesDesignated()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 2);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 3);

        // 所有奖品都指定给用户1
        foreach (var item in items)
        {
            await CreateDesignatedPrizeConfigAsync(goods.Id, item.Id, designatedUser.Id);
        }

        var prizePool = CreatePrizeProbabilitiesByRealPro(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolStrict(prizePool, normalUser.Id, designatedPrizes);

        // Assert - 严格模式无兜底，奖品池为空
        Assert.Empty(filteredPool);
    }

    /// <summary>
    /// 无限赏：多个指定奖品配置测试
    /// Requirements: 3.3, 3.4
    /// </summary>
    [Fact]
    public async Task InfiniteLottery_MultipleDesignatedPrizes_StrictFiltering()
    {
        // Arrange
        var user1 = await CreateTestUserAsync(1, "用户1");
        var user2 = await CreateTestUserAsync(2, "用户2");
        var user3 = await CreateTestUserAsync(3, "用户3");
        var goods = await CreateTestGoodsAsync(1, type: 2);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);

        await CreateDesignatedPrizeConfigAsync(goods.Id, items[0].Id, user1.Id);
        await CreateDesignatedPrizeConfigAsync(goods.Id, items[1].Id, user2.Id);

        var prizePool = CreatePrizeProbabilitiesByRealPro(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var user1Pool = _lotteryEngine.FilterPrizePoolStrict(prizePool, user1.Id, designatedPrizes);
        var user2Pool = _lotteryEngine.FilterPrizePoolStrict(prizePool, user2.Id, designatedPrizes);
        var user3Pool = _lotteryEngine.FilterPrizePoolStrict(prizePool, user3.Id, designatedPrizes);

        // Assert
        Assert.Equal(4, user1Pool.Count);
        Assert.Contains(user1Pool, p => p.GoodsItemId == items[0].Id);
        Assert.DoesNotContain(user1Pool, p => p.GoodsItemId == items[1].Id);

        Assert.Equal(4, user2Pool.Count);
        Assert.Contains(user2Pool, p => p.GoodsItemId == items[1].Id);
        Assert.DoesNotContain(user2Pool, p => p.GoodsItemId == items[0].Id);

        Assert.Equal(3, user3Pool.Count);
        Assert.DoesNotContain(user3Pool, p => p.GoodsItemId == items[0].Id);
        Assert.DoesNotContain(user3Pool, p => p.GoodsItemId == items[1].Id);
    }

    #endregion

    #region 配置状态测试

    /// <summary>
    /// 测试禁用的指定中奖配置不生效
    /// Requirements: 1.3
    /// </summary>
    [Fact]
    public async Task DisabledDesignatedPrizeConfig_ShouldNotAffectFiltering()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 1);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);

        // 创建禁用的配置
        await CreateDesignatedPrizeConfigAsync(goods.Id, items[0].Id, designatedUser.Id, isActive: false);

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var normalUserPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, normalUser.Id, designatedPrizes);

        // Assert - 禁用的配置不生效，普通用户可以看到所有奖品
        Assert.Equal(5, normalUserPool.Count);
        Assert.Contains(normalUserPool, p => p.GoodsItemId == items[0].Id);
    }

    /// <summary>
    /// 测试无指定中奖配置时返回原始奖品池
    /// </summary>
    [Fact]
    public async Task NoDesignatedPrizeConfig_ReturnsOriginalPool()
    {
        // Arrange
        var user = await CreateTestUserAsync(1, "用户");
        var goods = await CreateTestGoodsAsync(1, type: 1);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 5);

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = new Dictionary<int, int>(); // 空配置

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, user.Id, designatedPrizes);

        // Assert
        Assert.Equal(prizePool.Count, filteredPool.Count);
    }

    #endregion

    #region 边界情况测试

    /// <summary>
    /// 测试空奖品池
    /// </summary>
    [Fact]
    public void EmptyPrizePool_ReturnsEmptyPool()
    {
        // Arrange
        var prizePool = new List<PrizeProbability>();
        var designatedPrizes = new Dictionary<int, int> { { 1, 1 } };

        // Act
        var filteredPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, 1, designatedPrizes);

        // Assert
        Assert.Empty(filteredPool);
    }

    /// <summary>
    /// 测试单个奖品且为指定奖品的情况（有限赏兜底）
    /// </summary>
    [Fact]
    public async Task SingleDesignatedPrize_FiniteLottery_FallbackTriggered()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 1);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 1);
        await CreateDesignatedPrizeConfigAsync(goods.Id, items[0].Id, designatedUser.Id);

        var prizePool = CreatePrizeProbabilities(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var normalUserPool = _lotteryEngine.FilterPrizePoolWithFallback(prizePool, normalUser.Id, designatedPrizes);

        // Assert - 兜底机制触发
        Assert.Single(normalUserPool);
    }

    /// <summary>
    /// 测试单个奖品且为指定奖品的情况（无限赏严格模式）
    /// </summary>
    [Fact]
    public async Task SingleDesignatedPrize_InfiniteLottery_EmptyPool()
    {
        // Arrange
        var designatedUser = await CreateTestUserAsync(1, "指定用户");
        var normalUser = await CreateTestUserAsync(2, "普通用户");
        var goods = await CreateTestGoodsAsync(1, type: 2);
        var items = await CreateTestGoodsItemsAsync(goods.Id, 1);
        await CreateDesignatedPrizeConfigAsync(goods.Id, items[0].Id, designatedUser.Id);

        var prizePool = CreatePrizeProbabilitiesByRealPro(items);
        var designatedPrizes = await _dbContext.GoodsDesignatedPrizes
            .Where(dp => dp.GoodsId == goods.Id && dp.IsActive)
            .ToDictionaryAsync(dp => dp.GoodsItemId, dp => dp.UserId);

        // Act
        var normalUserPool = _lotteryEngine.FilterPrizePoolStrict(prizePool, normalUser.Id, designatedPrizes);

        // Assert - 严格模式无兜底
        Assert.Empty(normalUserPool);
    }

    #endregion
}
