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

    public DesignatedPrizeServiceTests()
    {
        var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        _dbContext = new HoneyBoxDbContext(options);
        _mockLogger = new Mock<ILogger<DesignatedPrizeService>>();
        _service = new DesignatedPrizeService(_dbContext, _mockLogger.Object);
    }

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

    #region GetByGoodsIdAsync Tests

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

        // Act
        var result = await _service.GetByGoodsIdAsync(goods.Id);

        // Assert
        Assert.NotNull(result);
        Assert.Single(result);
    }

    [Fact]
    public async Task GetByGoodsIdAsync_WithNonExistingGoods_ThrowsException()
    {
        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.GetByGoodsIdAsync(99999));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("盒子不存在", exception.Message);
    }


    [Fact]
    public async Task GetByGoodsIdAsync_WithDeletedGoods_ThrowsException()
    {
        // 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 & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.GetByGoodsIdAsync(deletedGoods.Id));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
    }

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

        // Act
        var result = await _service.GetByGoodsIdAsync(goods.Id);

        // Assert
        Assert.NotNull(result);
        var config = result.First();
        Assert.NotNull(config.GoodsItemTitle);
        Assert.NotNull(config.UserNickname);
    }

    #endregion

    #region CreateAsync Tests

    [Fact]
    public async Task CreateAsync_WithValidRequest_CreatesConfig()
    {
        // Arrange
        await SeedGoodsWithPrizesAndUsersAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var prize = await _dbContext.GoodsItems.FirstAsync(gi => gi.GoodsId == goods.Id);
        var user = await _dbContext.Users.FirstAsync();

        var request = new CreateDesignatedPrizeRequest
        {
            GoodsItemId = prize.Id,
            UserId = user.Id,
            Remark = "测试备注"
        };

        // Act
        var result = await _service.CreateAsync(goods.Id, request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(goods.Id, result.GoodsId);
        Assert.Equal(prize.Id, result.GoodsItemId);
        Assert.Equal(user.Id, result.UserId);
        Assert.True(result.IsActive);
        Assert.Equal("测试备注", result.Remark);
    }

    [Fact]
    public async Task CreateAsync_WithNonExistingGoods_ThrowsException()
    {
        // Arrange
        var request = new CreateDesignatedPrizeRequest
        {
            GoodsItemId = 1,
            UserId = 1
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.CreateAsync(99999, request));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("盒子不存在", exception.Message);
    }

    [Fact]
    public async Task CreateAsync_WithNonExistingPrize_ThrowsException()
    {
        // Arrange
        await SeedGoodsWithPrizesAndUsersAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var user = await _dbContext.Users.FirstAsync();

        var request = new CreateDesignatedPrizeRequest
        {
            GoodsItemId = 99999,
            UserId = user.Id
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.CreateAsync(goods.Id, request));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("奖品不存在", exception.Message);
    }

    [Fact]
    public async Task CreateAsync_WithNonExistingUser_ThrowsException()
    {
        // Arrange
        await SeedGoodsWithPrizesAndUsersAsync();
        var goods = await _dbContext.Goods.FirstAsync();
        var prize = await _dbContext.GoodsItems.FirstAsync(gi => gi.GoodsId == goods.Id);

        var request = new CreateDesignatedPrizeRequest
        {
            GoodsItemId = prize.Id,
            UserId = 99999
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.CreateAsync(goods.Id, request));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("用户不存在", exception.Message);
    }

    [Fact]
    public async Task CreateAsync_WithDuplicateConfig_ThrowsException()
    {
        // Arrange
        await SeedTestDataAsync();
        var existingConfig = await _dbContext.GoodsDesignatedPrizes.FirstAsync();
        var user = await _dbContext.Users.FirstAsync();

        var request = new CreateDesignatedPrizeRequest
        {
            GoodsItemId = existingConfig.GoodsItemId,
            UserId = user.Id
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.CreateAsync(existingConfig.GoodsId, request));
        Assert.Equal(BusinessErrorCodes.Conflict, exception.Code);
        Assert.Contains("该奖品已配置指定用户", exception.Message);
    }

    #endregion

    #region UpdateAsync Tests

    [Fact]
    public async Task UpdateAsync_WithValidRequest_UpdatesConfig()
    {
        // Arrange
        await SeedTestDataAsync();
        var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync();

        var request = new UpdateDesignatedPrizeRequest
        {
            IsActive = false,
            Remark = "更新后的备注"
        };

        // Act
        var result = await _service.UpdateAsync(config.Id, request);

        // Assert
        Assert.NotNull(result);
        Assert.False(result.IsActive);
        Assert.Equal("更新后的备注", result.Remark);
    }

    [Fact]
    public async Task UpdateAsync_WithNewUserId_UpdatesUser()
    {
        // Arrange
        await SeedTestDataAsync();
        var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync();
        
        // Add another user
        var newUser = new User
        {
            OpenId = "new_openid",
            Uid = "new_uid",
            Nickname = "新用户",
            HeadImg = "http://test.com/new.jpg",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Users.Add(newUser);
        await _dbContext.SaveChangesAsync();

        var request = new UpdateDesignatedPrizeRequest
        {
            UserId = newUser.Id
        };

        // Act
        var result = await _service.UpdateAsync(config.Id, request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(newUser.Id, result.UserId);
    }

    [Fact]
    public async Task UpdateAsync_WithNonExistingConfig_ThrowsException()
    {
        // Arrange
        var request = new UpdateDesignatedPrizeRequest
        {
            IsActive = false
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.UpdateAsync(99999, request));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("配置不存在", exception.Message);
    }

    [Fact]
    public async Task UpdateAsync_WithNonExistingNewUser_ThrowsException()
    {
        // Arrange
        await SeedTestDataAsync();
        var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync();

        var request = new UpdateDesignatedPrizeRequest
        {
            UserId = 99999
        };

        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.UpdateAsync(config.Id, request));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("用户不存在", exception.Message);
    }

    #endregion

    #region DeleteAsync Tests

    [Fact]
    public async Task DeleteAsync_WithExistingConfig_DeletesConfig()
    {
        // Arrange
        await SeedTestDataAsync();
        var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync();
        var configId = config.Id;

        // Act
        var result = await _service.DeleteAsync(configId);

        // Assert
        Assert.True(result);
        var deletedConfig = await _dbContext.GoodsDesignatedPrizes.FindAsync(configId);
        Assert.Null(deletedConfig);
    }

    [Fact]
    public async Task DeleteAsync_WithNonExistingConfig_ThrowsException()
    {
        // Act & Assert
        var exception = await Assert.ThrowsAsync<BusinessException>(
            () => _service.DeleteAsync(99999));
        Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
        Assert.Contains("配置不存在", exception.Message);
    }

    #endregion

    #region Helper Methods

    private async Task SeedTestDataAsync()
    {
        // Create user
        var user = new User
        {
            OpenId = "test_openid",
            Uid = "test_uid",
            Nickname = "测试用户",
            HeadImg = "http://test.com/avatar.jpg",
            Mobile = "13800138000",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Users.Add(user);
        await _dbContext.SaveChangesAsync();

        // Create goods
        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,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Goods.Add(goods);
        await _dbContext.SaveChangesAsync();

        // Create prize
        var prize = new GoodsItem
        {
            GoodsId = goods.Id,
            Num = 1,
            Title = "A赏",
            ImgUrl = "http://test.com/prize.jpg",
            Stock = 1,
            SurplusStock = 1,
            Price = 500,
            Money = 300,
            ScMoney = 250,
            RealPro = 1,
            GoodsType = 1,
            PrizeCode = "PC001",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.GoodsItems.Add(prize);
        await _dbContext.SaveChangesAsync();

        // Create designated prize config
        var config = new GoodsDesignatedPrize
        {
            GoodsId = goods.Id,
            GoodsItemId = prize.Id,
            UserId = user.Id,
            IsActive = true,
            Remark = "测试配置",
            CreatedAt = DateTime.Now
        };
        _dbContext.GoodsDesignatedPrizes.Add(config);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedGoodsWithPrizesAndUsersAsync()
    {
        // Create user
        var user = new User
        {
            OpenId = "test_openid",
            Uid = "test_uid",
            Nickname = "测试用户",
            HeadImg = "http://test.com/avatar.jpg",
            Mobile = "13800138000",
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Users.Add(user);
        await _dbContext.SaveChangesAsync();

        // Create goods
        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,
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now
        };
        _dbContext.Goods.Add(goods);
        await _dbContext.SaveChangesAsync();

        // Create prizes
        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,
                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,
                PrizeCode = "PC002",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };
        _dbContext.GoodsItems.AddRange(prizes);
        await _dbContext.SaveChangesAsync();
    }

    #endregion
}
