using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models;
using HoneyBox.Model.Models.Goods;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace HoneyBox.Core.Services;

/// <summary>
/// 商品服务实现
/// </summary>
public class GoodsService : IGoodsService
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly IGoodsCacheService _cacheService;
    private readonly ILogger<GoodsService> _logger;

    // 抽奖赏品ID范围 [10, 33]
    private static readonly int[] ShangPrizeIdRange = { 10, 33 };
    // 统计次数赏品ID范围 [10, 38]
    private static readonly int[] ShangCountIdRange = { 10, 38 };
    // 赠送赏品ID (不计入概率)
    private static readonly int[] ShangGiveIds = { 1, 2, 3, 4, 5 };

    public GoodsService(
        HoneyBoxDbContext dbContext,
        IGoodsCacheService cacheService,
        ILogger<GoodsService> logger)
    {
        _dbContext = dbContext;
        _cacheService = cacheService;
        _logger = logger;
    }

    // 缓存过期时间（商品详情仍使用缓存）
    private static readonly TimeSpan GoodsDetailCacheExpiry = TimeSpan.FromMinutes(5);

    /// <inheritdoc />
    public async Task<PageResponse<GoodsListDto>> GetGoodsListAsync(GoodsListRequest request, int userId)
    {
        var page = request.Page > 0 ? request.Page : 1;
        var pageSize = request.PageSize > 0 ? request.PageSize : 15;
        var type = request.Type;

        // Type 10 特殊分页
        if (type == 10)
        {
            pageSize = 999;
        }

        // 商品列表不使用缓存，确保库存数据实时更新

        // 构建基础查询 - 排除已删除的商品
        var query = _dbContext.Goods
            .Where(g => g.Status == 1 && g.ShowIs == 0 && g.DeletedAt == null);

        // 类型过滤
        query = ApplyTypeFilter(query, type);

        // 解锁金额过滤
        query = await ApplyUnlockAmountFilterAsync(query, userId);

        // 获取商品类型映射
        var goodsTypesMap = await GetGoodsTypesMapAsync();

        // 获取总数
        var total = await query.CountAsync();

        // 排序和分页
        var goods = await query
            .OrderByDescending(g => g.Sort)
            .ThenByDescending(g => g.Id)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(g => new
            {
                g.Id,
                g.Title,
                g.ImgUrl,
                g.Price,
                g.Type,
                g.Stock,
                g.SaleStock,
                g.Status,
                g.LockIs,
                g.IsShouZhe,
                g.NewIs
            })
            .ToListAsync();

        if (!goods.Any())
        {
            return new PageResponse<GoodsListDto>
            {
                Data = new List<GoodsListDto>(),
                Total = 0,
                Page = page,
                PageSize = pageSize,
                LastPage = 0
            };
        }

        // 获取所有商品ID
        var goodsIds = goods.Select(g => g.Id).ToList();

        // 批量获取Type=10商品的库存信息
        var type10GoodsIds = goods.Where(g => g.Type == 10).Select(g => g.Id).ToList();
        var goodsListStockMap = new Dictionary<int, (int Stock, int SurplusStock)>();
        
        if (type10GoodsIds.Any())
        {
            var stockData = await _dbContext.GoodsItems
                .Where(gi => type10GoodsIds.Contains(gi.GoodsId) 
                    && gi.Num == 1 
                    && gi.ShangId >= ShangPrizeIdRange[0] 
                    && gi.ShangId <= ShangPrizeIdRange[1])
                .GroupBy(gi => gi.GoodsId)
                .Select(g => new
                {
                    GoodsId = g.Key,
                    Stock = g.Sum(x => x.Stock),
                    SurplusStock = g.Sum(x => x.SurplusStock)
                })
                .ToListAsync();

            foreach (var item in stockData)
            {
                goodsListStockMap[item.GoodsId] = (item.Stock, item.SurplusStock);
            }
        }

        // 构建结果列表
        var result = new List<GoodsListDto>();
        foreach (var g in goods)
        {
            var dto = new GoodsListDto
            {
                Id = g.Id,
                Title = g.Title,
                ImgUrl = FormatImageUrl(g.ImgUrl),
                Price = g.Price.ToString("0.##"),
                Type = g.Type,
                Stock = g.Stock,
                SaleStock = g.Stock - g.SaleStock, // 剩余库存
                Status = g.Status,
                LockIs = g.LockIs,
                IsShouZhe = g.IsShouZhe,
                NewIs = g.NewIs,
                NeedDrawNum = g.Type == 7 ? 1 : 0
            };

            // Type=10 特殊处理库存
            if (g.Type == 10 && goodsListStockMap.TryGetValue(g.Id, out var stockInfo))
            {
                dto.Stock = stockInfo.Stock;
                dto.SaleStock = stockInfo.SurplusStock;
            }

            // 获取参与次数
            dto.JoinCount = await GetJoinCountAsync(g.Id, g.Type);

            // 设置类型文字
            if (goodsTypesMap.TryGetValue(g.Type, out var typeText))
            {
                dto.TypeText = typeText;
            }

            result.Add(dto);
        }

        var lastPage = pageSize > 0 ? (int)Math.Ceiling((double)total / pageSize) : 0;
        var response = new PageResponse<GoodsListDto>
        {
            Data = result,
            Total = total,
            Page = page,
            PageSize = pageSize,
            LastPage = lastPage
        };

        return response;
    }

    /// <summary>
    /// 应用类型过滤
    /// </summary>
    private IQueryable<Model.Entities.Good> ApplyTypeFilter(IQueryable<Model.Entities.Good> query, int type)
    {
        // 类型映射 - 使用 List<int> 避免 ReadOnlySpan 与 EF Core InMemory 的兼容性问题
        var typeMapping = new Dictionary<int, List<int>>
        {
            { 1, new List<int> { 1 } },
            { 2, new List<int> { 2 } },
            { 3, new List<int> { 3 } },
            { 5, new List<int> { 5 } },
            { 6, new List<int> { 6 } },
            { 7, new List<int> { 7 } },
            { 8, new List<int> { 8 } },
            { 9, new List<int> { 9 } },
            { 10, new List<int> { 10 } },
            { 11, new List<int> { 11 } },
            { 12, new List<int> { 12 } },
            { 15, new List<int> { 15 } },
            { 16, new List<int> { 16 } }
        };

        if (typeMapping.TryGetValue(type, out var types))
        {
            query = query.Where(g => types.Contains(g.Type));
        }
        else
        {
            // 默认类型: 2, 6, 8, 16
            var defaultTypes = new List<int> { 2, 6, 8, 16 };
            query = query.Where(g => defaultTypes.Contains(g.Type));
        }

        return query;
    }

    /// <summary>
    /// 应用解锁金额过滤
    /// </summary>
    private async Task<IQueryable<Model.Entities.Good>> ApplyUnlockAmountFilterAsync(
        IQueryable<Model.Entities.Good> query, int userId)
    {
        if (userId == 0)
        {
            // 未登录用户只能看到无门槛商品
            return query.Where(g => g.UnlockAmount == 0);
        }

        // 获取用户信息
        var user = await _dbContext.Users
            .Where(u => u.Id == userId)
            .Select(u => new { u.Id, u.IsTest })
            .FirstOrDefaultAsync();

        if (user == null)
        {
            return query.Where(g => g.UnlockAmount == 0);
        }

        // 计算用户消费金额
        decimal orderMoney;
        if (user.IsTest > 0)
        {
            // 推广账号使用折扣后金额
            orderMoney = await _dbContext.Orders
                .Where(o => o.Status == 1 && o.UserId == userId)
                .SumAsync(o => o.OrderZheTotal);
        }
        else
        {
            // 普通用户使用实际支付金额 + 余额抵扣
            var priceSum = await _dbContext.Orders
                .Where(o => o.Status == 1 && o.UserId == userId)
                .SumAsync(o => o.Price);
            var useMoneySum = await _dbContext.Orders
                .Where(o => o.Status == 1 && o.UserId == userId)
                .SumAsync(o => o.UseMoney);
            orderMoney = priceSum + useMoneySum;
        }

        return query.Where(g => g.UnlockAmount <= orderMoney);
    }

    /// <summary>
    /// 获取商品类型映射
    /// </summary>
    private async Task<Dictionary<int, string>> GetGoodsTypesMapAsync()
    {
        var types = await _dbContext.GoodsTypes
            .Select(t => new { t.Value, t.CornerText })
            .ToListAsync();

        return types
            .Where(t => !string.IsNullOrEmpty(t.CornerText))
            .ToDictionary(t => t.Value, t => t.CornerText!);
    }

    /// <summary>
    /// 获取商品参与次数
    /// </summary>
    private async Task<int> GetJoinCountAsync(int goodsId, int orderType)
    {
        // 先从缓存获取
        var joinCount = await _cacheService.GetJoinCountAsync(goodsId);
        if (joinCount >= 0)
        {
            return joinCount;
        }

        // 缓存未命中，从数据库查询
        // 注意：不过滤 num 字段，与 PHP 逻辑保持一致
        joinCount = await _dbContext.OrderItems
            .Where(oi => oi.GoodsId == goodsId
                && oi.ShangId >= ShangCountIdRange[0]
                && oi.ShangId <= ShangCountIdRange[1]
                && oi.OrderType == orderType)
            .CountAsync();

        // 设置缓存
        await _cacheService.SetJoinCountAsync(goodsId, joinCount);

        return joinCount;
    }

    /// <summary>
    /// 格式化图片URL
    /// </summary>
    private static string FormatImageUrl(string? imgUrl)
    {
        if (string.IsNullOrEmpty(imgUrl))
        {
            return string.Empty;
        }

        // 如果已经是完整URL，直接返回
        if (imgUrl.StartsWith("http://") || imgUrl.StartsWith("https://"))
        {
            return imgUrl;
        }

        // 否则添加基础URL (这里可以从配置读取)
        return imgUrl;
    }

    /// <inheritdoc />
    public async Task<GoodsDetailResponseDto> GetGoodsDetailAsync(int goodsId, int goodsNum, int userId)
    {
        // 1. 查询商品基本信息（排除已删除的商品）
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new
            {
                g.Id,
                g.Title,
                g.ImgUrl,
                g.ImgUrlBanner,
                g.ImgUrlDetail,
                g.Price,
                g.Stock,
                g.SaleStock,
                g.LockIs,
                g.Type,
                g.Status,
                g.SaleTime,
                g.IsShouZhe,
                g.QuanjuXiangou,
                g.DailyXiangou,
                g.CouponIs,
                g.CouponPro,
                g.IntegralIs,
                g.RageIs,
                g.Rage,
                g.LingzhuIs,
                g.LingzhuFan,
                g.CardNum,
                g.NewIs,
                g.GoodsDescribe
            })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            throw new InvalidOperationException("盒子不存在");
        }

        if (goods.Status != 1 && goods.Status != 3)
        {
            throw new InvalidOperationException("盒子已下架");
        }

        if (goodsNum > goods.Stock || goodsNum < 0)
        {
            throw new InvalidOperationException("箱号错误");
        }

        // 2. 自动选择箱号 (当goodsNum=0时)
        if (goodsNum == 0)
        {
            var availableBox = await _dbContext.GoodsItems
                .Where(gi => gi.GoodsId == goodsId
                    && gi.ShangId > 5
                    && gi.GoodsListId == 0)
                .GroupBy(gi => gi.Num)
                .Select(g => new
                {
                    Num = g.Key,
                    AllSurplusStock = g.Sum(x => x.SurplusStock)
                })
                .Where(x => x.AllSurplusStock > 0)
                .OrderBy(x => x.Num)
                .FirstOrDefaultAsync();

            goodsNum = availableBox?.Num ?? 1;
        }

        // 3. 获取商品类型文字
        var goodsType = await _dbContext.GoodsTypes
            .Where(t => t.Value == goods.Type)
            .Select(t => t.CornerText)
            .FirstOrDefaultAsync();

        // 4. 获取本箱子余量
        var goodsSurplus = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId
                && gi.Num == goodsNum
                && gi.GoodsListId == 0
                && gi.ShangId >= ShangPrizeIdRange[0]
                && gi.ShangId <= ShangPrizeIdRange[1])
            .GroupBy(gi => 1)
            .Select(g => new
            {
                Stock = g.Sum(x => x.Stock),
                SurplusStock = g.Sum(x => x.SurplusStock)
            })
            .FirstOrDefaultAsync();

        var allSurplusStock = goodsSurplus?.SurplusStock ?? 0;

        // 5. 获取所有奖品信息
        var goodsList = await GetGoodsListWithProbabilityAsync(goodsId, goodsNum, allSurplusStock);

        // 6. 获取锁箱信息
        var lockInfo = await GetLockInfoAsync(goodsId, goodsNum, goods.LockIs);

        // 7. 获取参与用户头像列表
        var joinUsers = await GetJoinUsersAsync(goodsId, goodsNum, goods.Type);

        // 8. 获取参与次数
        var joinCount = await GetJoinCountAsync(goodsId, goods.Type);

        // 9. 获取时间配置
        var (threeTime, fiveTime) = await GetTimeConfigAsync();

        // 10. 获取限购信息
        var limitInfo = await GetLimitInfoAsync(userId, goodsId, goodsNum, goods.Type, goods.DailyXiangou, goods.QuanjuXiangou);

        // 11. 获取收藏状态
        var collectionIs = 0;
        if (userId > 0)
        {
            collectionIs = await _dbContext.GoodsCollections
                .AnyAsync(c => c.UserId == userId && c.GoodsId == goodsId && c.Num == goodsNum) ? 1 : 0;
        }

        // 12. 构建响应
        var response = new GoodsDetailResponseDto
        {
            Goods = new GoodsInfoDto
            {
                Id = goods.Id,
                Title = goods.Title,
                ImgUrl = FormatImageUrl(goods.ImgUrl),
                ImgUrlDetail = FormatImageUrl(goods.ImgUrlDetail),
                ImgUrlBanner = FormatImageUrl(goods.ImgUrlBanner),
                Price = goods.Price.ToString("0.##"),
                Stock = goods.Stock,
                SaleStock = goods.SaleStock,
                SurplusStock = goods.Stock - goods.SaleStock,
                GoodslistStock = goodsSurplus?.Stock ?? 0,
                GoodslistSurplusStock = goodsSurplus?.SurplusStock ?? 0,
                Type = goods.Type,
                TypeText = goodsType,
                Status = goods.Status,
                LockIs = goods.LockIs,
                Num = goodsNum,
                Addtime = goods.SaleTime.HasValue ? goods.SaleTime.Value.ToString("MM-dd") : "",
                GoodsDescribe = goods.GoodsDescribe,
                CouponIs = goods.CouponIs,
                CouponPro = goods.CouponPro,
                IntegralIs = goods.IntegralIs,
                RageIs = goods.RageIs,
                Rage = goods.Rage,
                LingzhuIs = goods.LingzhuIs,
                LingzhuFan = goods.LingzhuFan,
                CardNum = goods.CardNum,
                IsShouZhe = goods.IsShouZhe,
                NewIs = goods.NewIs,
                CollectionIs = collectionIs,
                ThreeTime = threeTime,
                FiveTime = fiveTime,
                QuanjuXiangou = goods.QuanjuXiangou,
                DailyXiangou = goods.DailyXiangou
            },
            LockInfo = lockInfo,
            JoinUser = joinUsers,
            JoinCount = joinCount,
            GoodsList = goodsList,
            LimitInfo = limitInfo
        };

        return response;
    }

    /// <summary>
    /// 获取奖品列表并计算概率
    /// </summary>
    private async Task<List<GoodsListItemDto>> GetGoodsListWithProbabilityAsync(int goodsId, int goodsNum, int allSurplusStock)
    {
        // 获取所有奖品信息
        var goodsItems = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId
                && gi.Num == goodsNum
                && gi.GoodsListId == 0)
            .OrderByDescending(gi => gi.Sort)
            .ThenBy(gi => gi.ShangId)
            .ThenBy(gi => gi.Id)
            .Select(gi => new
            {
                gi.Id,
                gi.ShangId,
                gi.Title,
                gi.Stock,
                gi.SurplusStock,
                gi.ImgUrl,
                gi.GoodsType,
                gi.SaleTime,
                gi.Price,
                gi.ScMoney
            })
            .ToListAsync();

        // 获取所有赏品等级信息
        var shangIds = goodsItems.Where(x => x.ShangId.HasValue).Select(x => x.ShangId!.Value).Distinct().ToList();
        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => new ShangInfoDto
            {
                Id = p.Id,
                Title = p.Title,
                ImgUrl = FormatImageUrl(p.ImgUrl),
                Color = p.Color,
                SpecialImgUrl = FormatImageUrl(p.SpecialImgUrl)
            });

        // 计算概率
        var result = new List<GoodsListItemDto>();
        decimal proAll = 0;
        decimal proMax = 0;
        int proMaxIndex = 0;

        for (int i = 0; i < goodsItems.Count; i++)
        {
            var item = goodsItems[i];
            var shangId = item.ShangId ?? 0;
            var surplusStock = item.SurplusStock;

            string pro;
            decimal proNum = 0;

            // 赠送赏品不计入概率
            if (ShangGiveIds.Contains(shangId))
            {
                pro = ShangGiveText;
            }
            else
            {
                if (surplusStock > 0 && allSurplusStock > 0)
                {
                    proNum = Math.Round((decimal)surplusStock / allSurplusStock * 100, 2);
                    pro = $"概率：{RemoveTrailingZeros(proNum)}%";
                }
                else
                {
                    pro = "概率：0%";
                }
            }

            // 计算剩余概率
            proAll += proNum;
            if (proNum >= proMax)
            {
                proMax = proNum;
                proMaxIndex = i;
            }

            var dto = new GoodsListItemDto
            {
                Id = item.Id,
                ShangId = shangId,
                ShangInfo = shangId > 0 && shangInfos.TryGetValue(shangId, out var info) ? info : null,
                Title = item.Title,
                Stock = item.Stock,
                SurplusStock = surplusStock,
                ImgUrl = FormatImageUrl(item.ImgUrl),
                GoodsType = item.GoodsType,
                Price = (item.Price * 1).ToString("0.##"),
                ScMoney = item.ScMoney.ToString("0.##"),
                SaleTime = item.SaleTime.HasValue ? item.SaleTime.Value.ToString("yyyy-MM-dd") : null,
                Pro = pro,
                Children = item.GoodsType == 4 // goods_type=4 表示有子奖品
            };

            result.Add(dto);
        }

        // 概率不足100%时，补足到最大概率的奖品
        if (proMax > 0 && proAll < 100 && result.Count > proMaxIndex)
        {
            var surplusPro = 100 - proAll;
            var newPro = proMax + surplusPro;
            result[proMaxIndex].Pro = $"概率：{RemoveTrailingZeros(newPro)}%";
        }

        return result;
    }

    /// <summary>
    /// 获取锁箱信息
    /// </summary>
    private async Task<LockInfoDto> GetLockInfoAsync(int goodsId, int goodsNum, int lockIs)
    {
        var lockInfo = new LockInfoDto
        {
            LockIs = lockIs,
            GoodsLockUserNickname = null,
            GoodsLockUserHeadimg = null,
            GoodsLockSurplusTime = 0
        };

        if (lockIs == 0)
        {
            return lockInfo;
        }

        var goodsIdNum = $"{goodsId}_{goodsNum}";
        var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();

        var goodsLock = await _dbContext.GoodsLocks
            .Where(gl => gl.GoodsIdNum == goodsIdNum && gl.EndTime > currentTime)
            .OrderByDescending(gl => gl.Id)
            .FirstOrDefaultAsync();

        if (goodsLock != null)
        {
            lockInfo.GoodsLockSurplusTime = goodsLock.EndTime;

            var lockUser = await _dbContext.Users
                .Where(u => u.Id == goodsLock.UserId)
                .Select(u => new { u.Nickname, u.HeadImg })
                .FirstOrDefaultAsync();

            if (lockUser != null)
            {
                lockInfo.GoodsLockUserNickname = lockUser.Nickname;
                lockInfo.GoodsLockUserHeadimg = FormatImageUrl(lockUser.HeadImg);
            }
        }

        return lockInfo;
    }

    /// <summary>
    /// 获取参与用户头像列表
    /// </summary>
    private async Task<List<string>> GetJoinUsersAsync(int goodsId, int goodsNum, int orderType)
    {
        // 使用子查询获取每个用户的最新订单ID，然后按此排序
        var userIds = await _dbContext.OrderItems
            .Where(oi => oi.GoodsId == goodsId
                && oi.Num == goodsNum
                && oi.ShangId >= ShangCountIdRange[0]
                && oi.ShangId <= ShangCountIdRange[1]
                && oi.OrderType == orderType)
            .GroupBy(oi => oi.UserId)
            .Select(g => new { UserId = g.Key, MaxId = g.Max(x => x.Id) })
            .OrderByDescending(x => x.MaxId)
            .Take(5)
            .Select(x => x.UserId)
            .ToListAsync();

        if (!userIds.Any())
        {
            return new List<string>();
        }

        var users = await _dbContext.Users
            .Where(u => userIds.Contains(u.Id))
            .Select(u => new { u.Id, u.HeadImg })
            .ToListAsync();

        // 保持原始顺序
        return userIds
            .Select(id => users.FirstOrDefault(u => u.Id == id)?.HeadImg)
            .Where(img => !string.IsNullOrEmpty(img))
            .Select(img => FormatImageUrl(img!))
            .ToList();
    }

    /// <summary>
    /// 获取时间配置
    /// </summary>
    private async Task<(int ThreeTime, int FiveTime)> GetTimeConfigAsync()
    {
        var config = await _dbContext.Configs
            .Where(c => c.ConfigKey == "base")
            .Select(c => c.ConfigValue)
            .FirstOrDefaultAsync();

        if (string.IsNullOrEmpty(config))
        {
            return (0, 0);
        }

        try
        {
            var configObj = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(config);
            var threeTime = 0;
            var fiveTime = 0;

            if (configObj != null)
            {
                if (configObj.TryGetValue("three_time", out var threeTimeObj))
                {
                    int.TryParse(threeTimeObj?.ToString(), out threeTime);
                }
                if (configObj.TryGetValue("five_time", out var fiveTimeObj))
                {
                    int.TryParse(fiveTimeObj?.ToString(), out fiveTime);
                }
            }

            return (threeTime, fiveTime);
        }
        catch
        {
            return (0, 0);
        }
    }

    /// <summary>
    /// 获取限购信息
    /// </summary>
    private async Task<LimitInfoDto> GetLimitInfoAsync(int userId, int goodsId, int goodsNum, int orderType, int dailyXiangou, int quanjuXiangou)
    {
        var limitInfo = new LimitInfoDto
        {
            DailyXiangou = dailyXiangou,
            DailyBought = 0,
            DailyRemaining = dailyXiangou,
            QuanjuXiangou = quanjuXiangou,
            GlobalBought = 0,
            GlobalRemaining = quanjuXiangou
        };

        if (userId <= 0)
        {
            return limitInfo;
        }

        // 计算今日已购数量
        if (dailyXiangou > 0)
        {
            var todayStart = DateTime.Today;
            var todayEnd = todayStart.AddDays(1);

            var dailyBought = await _dbContext.OrderItems
                .Where(oi => oi.GoodsId == goodsId
                    && oi.UserId == userId
                    && oi.Num == goodsNum
                    && oi.ShangId >= ShangCountIdRange[0]
                    && oi.ShangId <= ShangCountIdRange[1]
                    && oi.OrderType == orderType
                    && oi.CreatedAt >= todayStart
                    && oi.CreatedAt < todayEnd)
                .CountAsync();

            limitInfo.DailyBought = dailyBought;
            limitInfo.DailyRemaining = Math.Max(0, dailyXiangou - dailyBought);
        }

        // 计算全局已购数量
        if (quanjuXiangou > 0)
        {
            var globalBought = await _dbContext.OrderItems
                .Where(oi => oi.GoodsId == goodsId
                    && oi.UserId == userId
                    && oi.Num == goodsNum
                    && oi.ShangId >= ShangCountIdRange[0]
                    && oi.ShangId <= ShangCountIdRange[1]
                    && oi.OrderType == orderType)
                .CountAsync();

            limitInfo.GlobalBought = globalBought;
            limitInfo.GlobalRemaining = Math.Max(0, quanjuXiangou - globalBought);
        }

        return limitInfo;
    }

    /// <summary>
    /// 移除小数末尾的零
    /// </summary>
    private static string RemoveTrailingZeros(decimal value)
    {
        return value.ToString("0.##");
    }

    // 赠送赏品文字
    private const string ShangGiveText = "只赠不售";

    /// <inheritdoc />
    public async Task<List<GoodsChildrenDto>> GetGoodsChildrenAsync(int goodsId, int goodsNum, int goodsListId)
    {
        // 1. 验证参数
        if (goodsListId <= 0)
        {
            throw new InvalidOperationException("对应的宝箱ID不能为空");
        }

        // 2. 验证商品存在且状态有效
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new { g.Id, g.Title, g.Status })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            throw new InvalidOperationException("盒子不存在");
        }

        if (goods.Status != 1 && goods.Status != 3)
        {
            throw new InvalidOperationException("盒子已下架");
        }

        // 3. 查询所有子奖品信息
        var childItems = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId
                && gi.Num == goodsNum
                && gi.GoodsListId == goodsListId)
            .OrderByDescending(gi => gi.Sort)
            .ThenBy(gi => gi.ShangId)
            .ThenBy(gi => gi.Id)
            .Select(gi => new
            {
                gi.Id,
                gi.ShangId,
                gi.Title,
                gi.Stock,
                gi.SurplusStock,
                gi.ImgUrl,
                gi.GoodsType,
                gi.SaleTime,
                gi.Price,
                gi.RealPro,
                gi.ScMoney
            })
            .ToListAsync();

        if (!childItems.Any())
        {
            return new List<GoodsChildrenDto>();
        }

        // 4. 获取所有赏品等级信息
        var shangIds = childItems
            .Where(x => x.ShangId.HasValue)
            .Select(x => x.ShangId!.Value)
            .Distinct()
            .ToList();

        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => new ShangInfoDto
            {
                Id = p.Id,
                Title = p.Title,
                ImgUrl = FormatImageUrl(p.ImgUrl),
                Color = p.Color,
                SpecialImgUrl = FormatImageUrl(p.SpecialImgUrl)
            });

        // 5. 计算总剩余库存 (排除赠送赏品)
        var totalSurplusStock = childItems
            .Where(x => x.ShangId.HasValue && !ShangGiveIds.Contains(x.ShangId.Value))
            .Sum(x => x.SurplusStock);

        // 6. 构建结果列表并计算概率
        var result = new List<GoodsChildrenDto>();

        foreach (var item in childItems)
        {
            var shangId = item.ShangId ?? 0;
            var dto = new GoodsChildrenDto
            {
                Id = item.Id,
                ShangId = shangId,
                Title = item.Title,
                ImgUrl = FormatImageUrl(item.ImgUrl),
                GoodsType = item.GoodsType,
                SaleTime = item.SaleTime.HasValue ? item.SaleTime.Value.ToString("yyyy-MM-dd") : null,
                Price = (item.Price * 1).ToString("0.##"),
                ScMoney = item.ScMoney.ToString("0.##")
            };

            // 设置shang_info (shang_id=38时不返回)
            if (shangId > 0 && shangId != 38 && shangInfos.TryGetValue(shangId, out var info))
            {
                dto.ShangInfo = info;
            }

            // 计算概率
            string pro;
            decimal proNum = 0;

            if (item.Stock == 0)
            {
                // 库存为0时使用数据库中的real_pro
                proNum = Math.Round(item.RealPro, 2);
                pro = $"概率：{RemoveTrailingZeros(proNum)}%";
                dto.RealPro = RemoveTrailingZeros(proNum);
                dto.ProNum = RemoveTrailingZeros(proNum);
                // 库存为0时不返回stock和surplus_stock (保持默认值0，会被JsonIgnore)
            }
            else if (ShangGiveIds.Contains(shangId))
            {
                // 赠送赏品显示"只赠不售"
                pro = ShangGiveText;
                dto.RealPro = "0";
                dto.ProNum = "0";
                dto.Stock = item.Stock;
                dto.SurplusStock = item.SurplusStock;
            }
            else
            {
                // 正常计算概率
                if (item.SurplusStock > 0 && totalSurplusStock > 0)
                {
                    proNum = Math.Round((decimal)item.SurplusStock / totalSurplusStock * 100, 2);
                    pro = $"概率：{RemoveTrailingZeros(proNum)}%";
                }
                else
                {
                    pro = "概率：0%";
                }
                dto.RealPro = RemoveTrailingZeros(proNum);
                dto.ProNum = RemoveTrailingZeros(proNum);
                dto.Stock = item.Stock;
                dto.SurplusStock = item.SurplusStock;
            }

            dto.Pro = pro;
            result.Add(dto);
        }

        return result;
    }

    /// <inheritdoc />
    public async Task<GoodsExtendDto> GetGoodsExtendAsync(int goodsId, int goodsType)
    {
        // 1. 先尝试从goods_extensions表获取商品特定配置
        var goodsExtend = await _dbContext.GoodsExtensions
            .Where(ge => ge.GoodsId == goodsId)
            .Select(ge => new GoodsExtendDto
            {
                PayWechat = ge.PayWechat,
                PayBalance = ge.PayBalance,
                PayCurrency = ge.PayCurrency,
                PayCurrency2 = ge.PayCurrency2,
                PayCoupon = ge.PayCoupon,
                IsDeduction = ge.IsDeduction
            })
            .FirstOrDefaultAsync();

        if (goodsExtend != null)
        {
            return goodsExtend;
        }

        // 2. 如果没有商品特定配置，从goods_types表获取类型默认配置
        var typeConfig = await _dbContext.GoodsTypes
            .Where(gt => gt.Value == goodsType)
            .Select(gt => new GoodsExtendDto
            {
                PayWechat = gt.PayWechat,
                PayBalance = gt.PayBalance,
                PayCurrency = gt.PayCurrency,
                PayCurrency2 = gt.PayCurrency2,
                PayCoupon = gt.PayCoupon,
                IsDeduction = gt.IsDeduction
            })
            .FirstOrDefaultAsync();

        if (typeConfig != null)
        {
            return typeConfig;
        }

        // 3. 如果都没有找到，返回默认值（全部启用）
        return new GoodsExtendDto
        {
            PayWechat = 1,
            PayBalance = 1,
            PayCurrency = 1,
            PayCurrency2 = 1,
            PayCoupon = 1,
            IsDeduction = 1
        };
    }

    /// <inheritdoc />
    public async Task<List<BoxGroupDto>> GetBoxListAsync(int goodsId)
    {
        // 1. 验证商品存在且状态有效
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new { g.Id, g.Stock, g.Status })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            throw new InvalidOperationException("盒子不存在");
        }

        if (goods.Status != 1 && goods.Status != 3)
        {
            throw new InvalidOperationException("盒子已下架");
        }

        // 2. 计算分组（每10个一组）
        const int pageSize = 10;
        var totalBoxes = goods.Stock;
        var groupCount = (int)Math.Ceiling((double)totalBoxes / pageSize);

        var result = new List<BoxGroupDto>();
        for (int i = 0; i < groupCount; i++)
        {
            var startNum = (i * pageSize) + 1;
            var endNum = Math.Min((i * pageSize) + pageSize, totalBoxes);

            result.Add(new BoxGroupDto
            {
                Title = $"{startNum}-{endNum}",
                PageNo = i
            });
        }

        return result;
    }

    /// <inheritdoc />
    public async Task<List<BoxDetailDto>> GetBoxDetailAsync(int goodsId, int pageNo, int sort)
    {
        // 1. 验证商品存在且状态有效
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new { g.Id, g.Stock, g.Status })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            throw new InvalidOperationException("盒子不存在");
        }

        if (goods.Status != 1 && goods.Status != 3)
        {
            throw new InvalidOperationException("盒子已下架");
        }

        // 2. 计算起始和结束箱号
        const int pageSize = 10;
        if (pageNo < 0) pageNo = 0;

        var startNum = (pageNo * pageSize) + 1;
        var endNum = Math.Min((pageNo * pageSize) + pageSize, goods.Stock);

        // 3. 获取所有赏品等级信息（用于shang_info）
        var shangInfos = await _dbContext.PrizeLevels
            .ToDictionaryAsync(p => p.Id, p => new ShangInfoDto
            {
                Id = p.Id,
                Title = p.Title,
                ImgUrl = FormatImageUrl(p.ImgUrl),
                Color = p.Color,
                SpecialImgUrl = FormatImageUrl(p.SpecialImgUrl)
            });

        // 4. 批量查询所有箱号的奖品信息
        var allGoodsItems = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId
                && gi.Num >= startNum
                && gi.Num <= endNum)
            .OrderByDescending(gi => gi.Sort)
            .ThenBy(gi => gi.ShangId)
            .ThenBy(gi => gi.Id)
            .Select(gi => new
            {
                gi.Id,
                gi.Num,
                gi.ShangId,
                gi.Stock,
                gi.SurplusStock
            })
            .ToListAsync();

        // 5. 按箱号分组处理
        var result = new List<BoxDetailDto>();
        for (int boxNum = startNum; boxNum <= endNum; boxNum++)
        {
            var boxItems = allGoodsItems.Where(gi => gi.Num == boxNum).ToList();

            // 计算剩余库存（排除赠送赏品）
            var surplusAllStock = boxItems
                .Where(gi => gi.ShangId.HasValue && !ShangGiveIds.Contains(gi.ShangId.Value))
                .Sum(gi => gi.SurplusStock);

            if (surplusAllStock < 0) surplusAllStock = 0;

            // 构建奖品列表
            var goodsList = boxItems.Select(gi =>
            {
                var shangId = gi.ShangId ?? 0;
                return new BoxGoodsListDto
                {
                    Id = gi.Id,
                    ShangId = shangId,
                    ShangInfo = shangId > 0 && shangInfos.TryGetValue(shangId, out var info) ? info : null,
                    Stock = gi.Stock,
                    SurplusStock = gi.SurplusStock
                };
            }).ToList();

            result.Add(new BoxDetailDto
            {
                Num = boxNum,
                SurplusAllStock = surplusAllStock,
                GoodsList = goodsList
            });
        }

        // 6. 应用排序
        // sort: 0=箱号升序(默认), 1=箱号降序, 2=余量降序
        result = sort switch
        {
            1 => result.OrderByDescending(x => x.Num).ToList(),
            2 => result.OrderByDescending(x => x.SurplusAllStock).ToList(),
            _ => result.OrderBy(x => x.Num).ToList()
        };

        return result;
    }

    // 无限赏抽奖赏品ID范围 [34, 38]
    private static readonly int[] InfiniteShangPrizeIdRange = { 34, 38 };

    /// <inheritdoc />
    public async Task<InfiniteGoodsDetailResponseDto> GetInfiniteGoodsDetailAsync(int goodsId, int userId)
    {
        // 1. 查询商品基本信息
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new
            {
                g.Id,
                g.Title,
                g.ImgUrlDetail,
                g.Price,
                g.Stock,
                g.SaleStock,
                g.LockIs,
                g.Type,
                g.Status,
                g.CreatedAt,
                g.RageIs,
                g.Rage,
                g.ItemCardId,
                g.LingzhuIs,
                g.LingzhuShangId,
                g.IsShouZhe,
                g.DailyXiangou,
                g.QuanjuXiangou
            })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            throw new InvalidOperationException("盒子不存在");
        }

        if (goods.Status != 1)
        {
            throw new InvalidOperationException("盒子已下架");
        }

        // 2. 获取商品类型文字
        var goodsType = await _dbContext.GoodsTypes
            .Where(t => t.Value == goods.Type)
            .Select(t => t.CornerText)
            .FirstOrDefaultAsync();

        // 3. 获取参与次数
        var joinCount = await GetInfiniteJoinCountAsync(goodsId);

        // 4. 获取赏池分类
        var goodsPoolList = await GetInfiniteGoodsPoolListAsync(goodsId, goods.LingzhuIs, goods.LingzhuShangId);

        // 5. 获取奖品列表
        var goodsItemList = await GetInfiniteGoodsItemListAsync(goodsId);

        // 6. 获取限购信息
        var limitInfo = await GetInfiniteLimitInfoAsync(userId, goodsId, goods.Type, goods.DailyXiangou, goods.QuanjuXiangou);

        // 7. 获取收藏状态和用户信息
        var collectionIs = 0;
        var drawNum = 0;
        var userTest = 0;

        if (userId > 0)
        {
            collectionIs = await _dbContext.GoodsCollections
                .AnyAsync(c => c.UserId == userId && c.GoodsId == goodsId && c.Num == 0) ? 1 : 0;

            var userInfo = await _dbContext.Users
                .Where(u => u.Id == userId)
                .Select(u => new { u.DrawNum, u.IsTest })
                .FirstOrDefaultAsync();

            if (userInfo != null)
            {
                drawNum = userInfo.DrawNum ?? 0;
                userTest = userInfo.IsTest;
            }
            limitInfo.UserTest = userTest;
        }

        // 8. 确定单页ID
        var danyeId = goods.Type switch
        {
            2 => 8,
            8 => 14,
            16 => 21,
            17 => 8,
            _ => 8
        };

        // 9. 构建响应
        return new InfiniteGoodsDetailResponseDto
        {
            Goods = new InfiniteGoodsInfoDto
            {
                Id = goods.Id,
                Title = goods.Title,
                ImgUrlDetail = FormatImageUrl(goods.ImgUrlDetail),
                Price = goods.Price.ToString("0.##"),
                Stock = goods.Stock,
                SaleStock = goods.SaleStock,
                LockIs = goods.LockIs,
                Type = goods.Type,
                TypeText = goodsType,
                Status = goods.Status,
                Addtime = goods.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss"),
                RageIs = goods.RageIs,
                Rage = goods.Rage,
                ItemCardId = goods.ItemCardId,
                LingzhuIs = goods.LingzhuIs,
                LingzhuShangId = goods.LingzhuShangId,
                IsShouZhe = goods.IsShouZhe,
                DailyXiangou = goods.DailyXiangou,
                QuanjuXiangou = goods.QuanjuXiangou,
                NeedDrawNum = goods.Type == 7 ? 1 : 0,
                JoinUser = new List<string>(),
                JoinCount = joinCount,
                CollectionIs = collectionIs
            },
            GoodsList = goodsPoolList,
            GoodsListItems = goodsItemList,
            DrawNum = drawNum,
            DanyeId = danyeId,
            LimitInfo = limitInfo
        };
    }

    private async Task<int> GetInfiniteJoinCountAsync(int goodsId)
    {
        var joinCount = await _cacheService.GetJoinCountAsync(goodsId);
        if (joinCount >= 0)
        {
            return joinCount;
        }

        joinCount = await _dbContext.OrderItems
            .Where(oi => oi.GoodsId == goodsId
                && oi.ShangId >= InfiniteShangPrizeIdRange[0]
                && oi.ShangId <= InfiniteShangPrizeIdRange[1])
            .CountAsync();

        await _cacheService.SetJoinCountAsync(goodsId, joinCount);
        return joinCount;
    }

    private async Task<List<InfiniteGoodsPoolDto>> GetInfiniteGoodsPoolListAsync(int goodsId, int lingzhuIs, int lingzhuShangId)
    {
        var shangGroups = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId && gi.Num == 0 && gi.GoodsListId == 0)
            .GroupBy(gi => gi.ShangId)
            .Select(g => new { ShangId = g.Key, Items = g.ToList() })
            .ToListAsync();

        var shangIds = shangGroups.Where(x => x.ShangId.HasValue).Select(x => x.ShangId!.Value).Distinct().ToList();
        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => p);

        var result = new List<InfiniteGoodsPoolDto>();
        foreach (var group in shangGroups.OrderByDescending(g => g.Items.Max(i => i.Sort)).ThenBy(g => g.ShangId))
        {
            var shangId = group.ShangId ?? 0;
            var totalPro = group.Items.Sum(i => i.RealPro);

            var poolDto = new InfiniteGoodsPoolDto
            {
                GoodsId = goodsId,
                ShangId = shangId,
                Pro = $"概率:{totalPro:0.##}%",
                LingzhuShangId = (lingzhuIs == 1 && shangId == lingzhuShangId) ? 1 : 0
            };

            if (shangId > 0 && shangInfos.TryGetValue(shangId, out var shangInfo))
            {
                poolDto.ShangTitle = shangInfo.Title ?? "";
                poolDto.ShangImgUrl = FormatImageUrl(shangInfo.ImgUrl);
                poolDto.ShangColor = shangInfo.Color;
            }

            poolDto.GoodsList = group.Items.Select(i => new InfiniteGoodsItemDto
            {
                Id = i.Id,
                GoodsId = i.GoodsId,
                ShangId = i.ShangId ?? 0,
                Title = i.Title,
                ImgUrl = FormatImageUrl(i.ImgUrl),
                Price = i.Price.ToString("0.##"),
                RealPro = i.RealPro.ToString("0.##"),
                ScMoney = i.ScMoney.ToString("0.##"),
                GoodsType = i.GoodsType,
                SaleTime = i.SaleTime?.ToString("yyyy-MM-dd"),
                Doubling = i.Doubling,
                IsLingzhu = (lingzhuIs == 1 && shangId == lingzhuShangId) ? 1 : i.IsLingzhu
            }).ToList();

            result.Add(poolDto);
        }

        return result;
    }

    private async Task<List<InfiniteGoodsItemDto>> GetInfiniteGoodsItemListAsync(int goodsId)
    {
        var items = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId && gi.Num == 0 && gi.GoodsListId == 0)
            .OrderByDescending(gi => gi.Sort)
            .ThenBy(gi => gi.ShangId)
            .ThenBy(gi => gi.Id)
            .ToListAsync();

        var shangIds = items.Where(x => x.ShangId.HasValue).Select(x => x.ShangId!.Value).Distinct().ToList();
        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => p);

        return items.Select(i =>
        {
            var shangId = i.ShangId ?? 0;
            var dto = new InfiniteGoodsItemDto
            {
                Id = i.Id,
                GoodsId = i.GoodsId,
                ShangId = shangId,
                Title = i.Title,
                ImgUrl = FormatImageUrl(i.ImgUrl),
                Price = i.Price.ToString("0.##"),
                RealPro = i.RealPro.ToString("0.##"),
                ScMoney = i.ScMoney.ToString("0.##"),
                GoodsType = i.GoodsType,
                SaleTime = i.SaleTime?.ToString("yyyy-MM-dd"),
                Doubling = i.Doubling,
                IsLingzhu = i.IsLingzhu
            };

            if (shangId > 0 && shangInfos.TryGetValue(shangId, out var shangInfo))
            {
                dto.ShangTitle = shangInfo.Title ?? "";
                dto.ShangImgUrl = FormatImageUrl(shangInfo.ImgUrl);
                dto.ShangColor = shangInfo.Color;
            }

            return dto;
        }).ToList();
    }

    private async Task<LimitInfoDto> GetInfiniteLimitInfoAsync(int userId, int goodsId, int orderType, int dailyXiangou, int quanjuXiangou)
    {
        var limitInfo = new LimitInfoDto
        {
            DailyXiangou = dailyXiangou,
            DailyBought = 0,
            DailyRemaining = dailyXiangou,
            QuanjuXiangou = quanjuXiangou,
            GlobalBought = 0,
            GlobalRemaining = quanjuXiangou
        };

        if (userId <= 0) return limitInfo;

        if (dailyXiangou > 0)
        {
            var todayStart = DateTime.Today;
            var dailyBought = await _dbContext.OrderItems
                .Where(oi => oi.GoodsId == goodsId
                    && oi.UserId == userId
                    && oi.Num == 0
                    && oi.ShangId >= InfiniteShangPrizeIdRange[0]
                    && oi.ShangId <= InfiniteShangPrizeIdRange[1]
                    && oi.OrderType == orderType
                    && oi.CreatedAt >= todayStart)
                .CountAsync();

            limitInfo.DailyBought = dailyBought;
            limitInfo.DailyRemaining = Math.Max(0, dailyXiangou - dailyBought);
        }

        if (quanjuXiangou > 0)
        {
            var globalBought = await _dbContext.OrderItems
                .Where(oi => oi.GoodsId == goodsId
                    && oi.UserId == userId
                    && oi.Num == 0
                    && oi.ShangId >= InfiniteShangPrizeIdRange[0]
                    && oi.ShangId <= InfiniteShangPrizeIdRange[1]
                    && oi.OrderType == orderType)
                .CountAsync();

            limitInfo.GlobalBought = globalBought;
            limitInfo.GlobalRemaining = Math.Max(0, quanjuXiangou - globalBought);
        }

        return limitInfo;
    }

    /// <inheritdoc />
    public async Task<ComboGoodsDetailResponseDto> GetComboGoodsDetailAsync(int goodsId, int type, int userId)
    {
        // 1. 查询商品基本信息
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new
            {
                g.Id,
                g.Title,
                g.ImgUrlDetail,
                g.Price,
                g.Stock,
                g.SaleStock,
                g.LockIs,
                g.Type,
                g.Status,
                g.CreatedAt,
                g.LianJiNum,
                g.LianJiShangId,
                g.IsShouZhe
            })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            throw new InvalidOperationException("盒子不存在");
        }

        if (goods.Status != 1)
        {
            throw new InvalidOperationException("盒子已下架");
        }

        if (type != 0 && type != 1)
        {
            throw new InvalidOperationException("参数错误");
        }

        // 2. 获取用户信息
        var userInfo = userId > 0 ? await _dbContext.Users
            .Where(u => u.Id == userId)
            .Select(u => new { u.MbNumber, u.DrawNum })
            .FirstOrDefaultAsync() : null;

        // 3. 获取收藏状态
        var collectionIs = userId > 0 ? await _dbContext.GoodsCollections
            .AnyAsync(c => c.UserId == userId && c.GoodsId == goodsId && c.Num == 0) ? 1 : 0 : 0;

        // 4. 获取参与用户和次数
        var joinUsers = await GetComboJoinUsersAsync(goodsId, goods.Type);
        var joinCount = await GetComboJoinCountAsync(goodsId, goods.Type);

        // 5. 获取赏池分类 (按lian_ji_type过滤)
        var goodsPoolList = await GetComboGoodsPoolListAsync(goodsId, type);

        // 6. 获取奖品列表
        var goodsItemList = await GetComboGoodsItemListAsync(goodsId, type);

        // 7. 获取基础配置
        var baseConfig = await GetBaseConfigAsync();

        return new ComboGoodsDetailResponseDto
        {
            Goods = new ComboGoodsInfoDto
            {
                Id = goods.Id,
                Title = goods.Title,
                ImgUrlDetail = FormatImageUrl(goods.ImgUrlDetail),
                Price = goods.Price.ToString("0.##"),
                Stock = goods.Stock,
                SaleStock = goods.SaleStock,
                LockIs = goods.LockIs,
                Type = goods.Type,
                Status = goods.Status,
                Addtime = goods.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss"),
                LianJiNum = goods.LianJiNum,
                LianJiShangId = goods.LianJiShangId,
                IsShouZhe = goods.IsShouZhe,
                MaxFa = baseConfig.LianjiMaxNum,
                UserMbNumber = userInfo?.MbNumber ?? 0,
                UserLianJiNumber = 0, // TODO: 需要user_goods_lian_ji表
                CollectionIs = collectionIs,
                JoinUser = joinUsers,
                JoinCount = joinCount
            },
            GoodsList = goodsPoolList,
            GoodsListItems = goodsItemList,
            DrawNum = userInfo?.DrawNum ?? 0
        };
    }

    private async Task<List<string>> GetComboJoinUsersAsync(int goodsId, int orderType)
    {
        var userIds = await _dbContext.OrderItems
            .Where(oi => oi.GoodsId == goodsId
                && oi.OrderType == orderType
                && oi.Source == 1)
            .OrderByDescending(oi => oi.Id)
            .Select(oi => oi.UserId)
            .Distinct()
            .Take(5)
            .ToListAsync();

        if (!userIds.Any()) return new List<string>();

        var users = await _dbContext.Users
            .Where(u => userIds.Contains(u.Id))
            .Select(u => new { u.Id, u.HeadImg })
            .ToListAsync();

        return userIds
            .Select(id => users.FirstOrDefault(u => u.Id == id)?.HeadImg)
            .Where(img => !string.IsNullOrEmpty(img))
            .Select(img => FormatImageUrl(img!))
            .ToList();
    }

    private async Task<int> GetComboJoinCountAsync(int goodsId, int orderType)
    {
        var joinCount = await _cacheService.GetJoinCountAsync(goodsId);
        if (joinCount >= 0) return joinCount;

        joinCount = await _dbContext.OrderItems
            .Where(oi => oi.GoodsId == goodsId
                && oi.ShangId >= InfiniteShangPrizeIdRange[0]
                && oi.ShangId <= InfiniteShangPrizeIdRange[1]
                && oi.OrderType == orderType
                && oi.Source == 1)
            .CountAsync();

        await _cacheService.SetJoinCountAsync(goodsId, joinCount);
        return joinCount;
    }

    private async Task<List<InfiniteGoodsPoolDto>> GetComboGoodsPoolListAsync(int goodsId, int lianJiType)
    {
        var shangGroups = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId && gi.Num == 0 && gi.LianJiType == lianJiType)
            .GroupBy(gi => gi.ShangId)
            .Select(g => new { ShangId = g.Key, Items = g.ToList() })
            .ToListAsync();

        var shangIds = shangGroups.Where(x => x.ShangId.HasValue).Select(x => x.ShangId!.Value).Distinct().ToList();
        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => p);

        var result = new List<InfiniteGoodsPoolDto>();
        foreach (var group in shangGroups.OrderByDescending(g => g.Items.Max(i => i.Sort)).ThenBy(g => g.ShangId))
        {
            var shangId = group.ShangId ?? 0;
            var totalPro = group.Items.Sum(i => i.RealPro);

            var poolDto = new InfiniteGoodsPoolDto
            {
                GoodsId = goodsId,
                ShangId = shangId,
                Pro = $"概率:{totalPro:0.##}%"
            };

            if (shangId > 0 && shangInfos.TryGetValue(shangId, out var shangInfo))
            {
                poolDto.ShangTitle = shangInfo.Title ?? "";
                poolDto.ShangImgUrl = FormatImageUrl(shangInfo.ImgUrl);
                poolDto.ShangColor = shangInfo.Color;
            }

            result.Add(poolDto);
        }

        return result;
    }

    private async Task<List<InfiniteGoodsItemDto>> GetComboGoodsItemListAsync(int goodsId, int lianJiType)
    {
        var items = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId && gi.Num == 0 && gi.LianJiType == lianJiType)
            .OrderByDescending(gi => gi.Sort)
            .ThenBy(gi => gi.ShangId)
            .ThenBy(gi => gi.Id)
            .ToListAsync();

        var shangIds = items.Where(x => x.ShangId.HasValue).Select(x => x.ShangId!.Value).Distinct().ToList();
        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => p);

        return items.Select(i =>
        {
            var shangId = i.ShangId ?? 0;
            var dto = new InfiniteGoodsItemDto
            {
                Id = i.Id,
                GoodsId = i.GoodsId,
                ShangId = shangId,
                Title = i.Title,
                ImgUrl = FormatImageUrl(i.ImgUrl),
                Price = i.Price.ToString("0.##"),
                RealPro = i.RealPro.ToString("0.##"),
                ScMoney = i.ScMoney.ToString("0.##"),
                GoodsType = i.GoodsType,
                Doubling = i.Doubling
            };

            if (shangId > 0 && shangInfos.TryGetValue(shangId, out var shangInfo))
            {
                dto.ShangTitle = shangInfo.Title ?? "";
                dto.ShangImgUrl = FormatImageUrl(shangInfo.ImgUrl);
                dto.ShangColor = shangInfo.Color;
            }

            return dto;
        }).ToList();
    }

    private async Task<(int LianjiMaxNum, int ThreeTime, int FiveTime)> GetBaseConfigAsync()
    {
        var config = await _dbContext.Configs
            .Where(c => c.ConfigKey == "base")
            .Select(c => c.ConfigValue)
            .FirstOrDefaultAsync();

        if (string.IsNullOrEmpty(config))
            return (20, 0, 0);

        try
        {
            var configObj = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(config);
            var lianjiMaxNum = 20;
            var threeTime = 0;
            var fiveTime = 0;

            if (configObj != null)
            {
                if (configObj.TryGetValue("lianji_max_num", out var maxNumObj))
                    int.TryParse(maxNumObj?.ToString(), out lianjiMaxNum);
                if (configObj.TryGetValue("three_time", out var threeTimeObj))
                    int.TryParse(threeTimeObj?.ToString(), out threeTime);
                if (configObj.TryGetValue("five_time", out var fiveTimeObj))
                    int.TryParse(fiveTimeObj?.ToString(), out fiveTime);
            }

            return (lianjiMaxNum, threeTime, fiveTime);
        }
        catch
        {
            return (20, 0, 0);
        }
    }

    /// <inheritdoc />
    public async Task<ShangLogResponseDto> GetShangLogAsync(int goodsId, int goodsNum, int shangId, int page, int pageSize)
    {
        // 1. 验证商品
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new { g.Id, g.Stock, g.Status, g.Type })
            .FirstOrDefaultAsync();

        if (goods == null)
            throw new InvalidOperationException("盒子不存在");

        if (goods.Status != 1 && goods.Status != 3)
            throw new InvalidOperationException("盒子已下架");

        // 2. 获取分类列表
        var categories = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId && gi.Num == goodsNum)
            .Select(gi => gi.ShangId)
            .Distinct()
            .ToListAsync();

        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => categories.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => p.Title ?? "");

        var categoryList = new List<CategoryDto> { new() { ShangId = 0, ShangTitle = "全部" } };
        categoryList.AddRange(categories
            .Where(s => s.HasValue)
            .OrderBy(s => s)
            .Select(s => new CategoryDto
            {
                ShangId = s!.Value,
                ShangTitle = shangInfos.TryGetValue(s.Value, out var title) ? title : ""
            }));

        // 3. 查询中奖记录
        var query = _dbContext.OrderItems
            .Where(oi => oi.GoodsId == goodsId
                && oi.Num == goodsNum
                && oi.OrderType == goods.Type
                && oi.Source == 1);

        if (shangId > 0)
            query = query.Where(oi => oi.ShangId == shangId);

        var total = await query.CountAsync();
        var lastPage = pageSize > 0 ? (int)Math.Ceiling((double)total / pageSize) : 0;

        var records = await query
            .OrderByDescending(oi => oi.Id)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(oi => new
            {
                oi.UserId,
                oi.GoodslistTitle,
                oi.GoodslistImgurl,
                oi.ShangId,
                oi.Addtime,
                oi.OrderId,
                oi.GoodslistId
            })
            .ToListAsync();

        // 4. 获取用户信息
        var userIds = records.Select(r => r.UserId).Distinct().ToList();
        var users = await _dbContext.Users
            .Where(u => userIds.Contains(u.Id))
            .ToDictionaryAsync(u => u.Id, u => new { u.Nickname, u.HeadImg });

        // 5. 获取赏品颜色
        var recordShangIds = records.Select(r => r.ShangId).Distinct().ToList();
        var shangColors = await _dbContext.PrizeLevels
            .Where(p => recordShangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => new { p.Title, p.Color });

        // 6. 构建结果
        var data = records.Select(r =>
        {
            var dto = new ShangLogItemDto
            {
                UserId = r.UserId,
                GoodslistTitle = r.GoodslistTitle ?? "",
                GoodslistImgurl = FormatImageUrl(r.GoodslistImgurl),
                ShangId = r.ShangId,
                Addtime = DateTimeOffset.FromUnixTimeSeconds(r.Addtime).LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                PrizeNum = 1
            };

            if (users.TryGetValue(r.UserId, out var user))
            {
                dto.UserInfo = new PrizeLogUserDto
                {
                    Nickname = user.Nickname ?? "",
                    HeadImg = FormatImageUrl(user.HeadImg)
                };
            }

            if (shangColors.TryGetValue(r.ShangId, out var shang))
            {
                dto.ShangTitle = shang.Title ?? "";
                dto.ShangColor = shang.Color ?? "";
            }

            return dto;
        }).ToList();

        return new ShangLogResponseDto
        {
            Category = categoryList,
            Data = data,
            LastPage = lastPage
        };
    }

    /// <inheritdoc />
    public async Task<LingZhuKingResponseDto> GetLingZhuKingAsync(int goodsId, int type, int page, int pageSize)
    {
        // 1. 获取商品信息
        var goods = await _dbContext.Goods
            .Where(g => g.Id == goodsId && g.DeletedAt == null)
            .Select(g => new { g.KingUserId, g.LingzhuFan, g.LingzhuShangId })
            .FirstOrDefaultAsync();

        if (goods == null)
        {
            return new LingZhuKingResponseDto
            {
                Goods = null,
                KingUser = null,
                List = new PageResponse<LingZhuListItemDto>(),
                LingGoodsList = new List<LingZhuGoodsItemDto>()
            };
        }

        // 2. 获取当前领主用户信息
        LingZhuKingUserDto? kingUser = null;
        if (goods.KingUserId > 0)
        {
            var user = await _dbContext.Users
                .Where(u => u.Id == goods.KingUserId)
                .Select(u => new { u.Id, u.Nickname, u.HeadImg })
                .FirstOrDefaultAsync();

            if (user != null)
            {
                // 查询领主的排名记录
                var kingRank = await _dbContext.GoodsKingRanks
                    .Where(r => r.UserId == goods.KingUserId && r.GoodsId == goodsId)
                    .OrderByDescending(r => r.Id)
                    .Select(r => new { r.Count, r.ZNums, r.OrderListId })
                    .FirstOrDefaultAsync();

                // 获取中奖奖品信息
                string jiangTitle = "";
                string jiangImg = "";
                if (kingRank != null && kingRank.OrderListId > 0)
                {
                    var orderItem = await _dbContext.OrderItems
                        .Where(oi => oi.Id == kingRank.OrderListId)
                        .Select(oi => new { oi.GoodslistTitle, oi.GoodslistImgurl })
                        .FirstOrDefaultAsync();
                    if (orderItem != null)
                    {
                        jiangTitle = orderItem.GoodslistTitle ?? "";
                        jiangImg = FormatImageUrl(orderItem.GoodslistImgurl);
                    }
                }

                kingUser = new LingZhuKingUserDto
                {
                    Id = user.Id,
                    Nickname = user.Nickname ?? "",
                    HeadImg = FormatImageUrl(user.HeadImg),
                    JiangTitle = jiangTitle,
                    JiangImg = jiangImg,
                    Count = kingRank?.Count ?? 0,
                    ZNums = kingRank?.ZNums ?? 0
                };
            }
        }

        // 3. 获取领主奖品列表
        var lingGoodsList = await _dbContext.GoodsItems
            .Where(gi => gi.GoodsId == goodsId
                && gi.Num == 0
                && (gi.ShangId == goods.LingzhuShangId || gi.IsLingzhu == 1))
            .Select(gi => new { gi.GoodsId, gi.ShangId, gi.Title, gi.ImgUrl })
            .ToListAsync();

        var shangIds = lingGoodsList.Where(x => x.ShangId.HasValue).Select(x => x.ShangId!.Value).Distinct().ToList();
        var shangInfos = await _dbContext.PrizeLevels
            .Where(p => shangIds.Contains(p.Id))
            .ToDictionaryAsync(p => p.Id, p => new ShangInfoDto
            {
                Id = p.Id,
                Title = p.Title ?? "",
                ImgUrl = FormatImageUrl(p.ImgUrl),
                Color = p.Color
            });

        var lingGoodsListDto = lingGoodsList.Select(gi => new LingZhuGoodsItemDto
        {
            GoodsId = gi.GoodsId,
            ShangId = gi.ShangId ?? 0,
            Title = gi.Title,
            ImgUrl = FormatImageUrl(gi.ImgUrl),
            ShangInfo = gi.ShangId.HasValue && shangInfos.TryGetValue(gi.ShangId.Value, out var info) ? info : null
        }).ToList();

        // 4. 获取挑战者列表或领主记录
        var listResponse = new PageResponse<LingZhuListItemDto>
        {
            Data = new List<LingZhuListItemDto>(),
            Total = 0,
            Page = page,
            PageSize = pageSize,
            LastPage = 0
        };

        if (type == 1)
        {
            // 挑战人数 - 按用户分组，获取每个用户最新的一条记录
            var distinctUserIds = await _dbContext.GoodsKingRanks
                .Where(r => r.GoodsId == goodsId)
                .Select(r => r.UserId)
                .Distinct()
                .ToListAsync();

            var total = distinctUserIds.Count;
            var lastPage = (int)Math.Ceiling((double)total / pageSize);

            // 获取分页后的用户ID
            var pagedUserIds = distinctUserIds
                .Skip((page - 1) * pageSize)
                .Take(pageSize)
                .ToList();

            // 获取这些用户的最新记录
            var rankList = new List<GoodsKingRank>();
            foreach (var userId in pagedUserIds)
            {
                var latestRank = await _dbContext.GoodsKingRanks
                    .Where(r => r.GoodsId == goodsId && r.UserId == userId)
                    .OrderByDescending(r => r.Id)
                    .FirstOrDefaultAsync();
                if (latestRank != null)
                {
                    rankList.Add(latestRank);
                }
            }

            var userList = await _dbContext.Users
                .Where(u => pagedUserIds.Contains(u.Id))
                .Select(u => new { u.Id, u.Nickname, u.HeadImg })
                .ToListAsync();
            var users = userList.ToDictionary(u => u.Id, u => new { u.Nickname, u.HeadImg });

            listResponse.Data = rankList.Select(r => new LingZhuListItemDto
            {
                UserId = r.UserId,
                Nickname = users.TryGetValue(r.UserId, out var u) ? u.Nickname ?? "" : "",
                HeadImg = users.TryGetValue(r.UserId, out var u2) ? FormatImageUrl(u2.HeadImg) : "",
                Addtime = r.Addtime,
                EndTime = r.EndTime,
                Time = ""
            }).ToList();
            listResponse.Total = total;
            listResponse.LastPage = lastPage;
        }
        else if (type == 2)
        {
            // 领主记录
            var query = _dbContext.GoodsKingRanks
                .Where(r => r.GoodsId == goodsId);

            var total = await query.CountAsync();
            var lastPage = (int)Math.Ceiling((double)total / pageSize);

            var rankList = await query
                .OrderByDescending(r => r.Id)
                .Skip((page - 1) * pageSize)
                .Take(pageSize)
                .ToListAsync();

            var userIds = rankList.Select(r => r.UserId).Distinct().ToList();
            var userList = await _dbContext.Users
                .Where(u => userIds.Contains(u.Id))
                .Select(u => new { u.Id, u.Nickname, u.HeadImg })
                .ToListAsync();
            var users = userList.ToDictionary(u => u.Id, u => new { u.Nickname, u.HeadImg });

            listResponse.Data = rankList.Select(r => new LingZhuListItemDto
            {
                UserId = r.UserId,
                Nickname = users.TryGetValue(r.UserId, out var u) ? u.Nickname ?? "" : "",
                HeadImg = users.TryGetValue(r.UserId, out var u2) ? FormatImageUrl(u2.HeadImg) : "",
                Addtime = r.Addtime,
                EndTime = r.EndTime,
                Time = CalculateTimeSpan(r.Addtime, r.EndTime > 0 ? r.EndTime : (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds())
            }).ToList();
            listResponse.Total = total;
            listResponse.LastPage = lastPage;
        }

        return new LingZhuKingResponseDto
        {
            Goods = new LingZhuGoodsDto
            {
                KingUserId = goods.KingUserId,
                LingzhuFan = goods.LingzhuFan,
                LingzhuShangId = goods.LingzhuShangId
            },
            KingUser = kingUser,
            List = listResponse,
            LingGoodsList = lingGoodsListDto
        };
    }

    /// <summary>
    /// 计算时间间隔的可读字符串
    /// </summary>
    private static string CalculateTimeSpan(int startTime, int endTime)
    {
        var seconds = endTime - startTime;
        if (seconds < 0) return "0秒";
        
        var days = seconds / 86400;
        var hours = (seconds % 86400) / 3600;
        var minutes = (seconds % 3600) / 60;
        var secs = seconds % 60;

        if (days > 0) return $"{days}天{hours}小时";
        if (hours > 0) return $"{hours}小时{minutes}分钟";
        if (minutes > 0) return $"{minutes}分钟{secs}秒";
        return $"{secs}秒";
    }
}
