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

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

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

        _rewardService = new RewardService(_dbContext, _mockLogger.Object);
    }

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

    #region GetRewardsAsync Tests

    [Fact]
    public async Task GetRewardsAsync_WithNoFilters_ReturnsAllRewards()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _rewardService.GetRewardsAsync(request);

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

    [Fact]
    public async Task GetRewardsAsync_WithTypeFilter_ReturnsFilteredRewards()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardListRequest { Page = 1, PageSize = 10, RewardType = 1 };

        // Act
        var result = await _rewardService.GetRewardsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(1, result.Total);
        Assert.All(result.List, r => Assert.Equal(1, r.RewardType));
    }

    [Fact]
    public async Task GetRewardsAsync_WithKeywordFilter_ReturnsFilteredRewards()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardListRequest { Page = 1, PageSize = 10, Keyword = "钻石" };

        // Act
        var result = await _rewardService.GetRewardsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(1, result.Total);
        Assert.Contains("钻石", result.List[0].Description);
    }

    [Fact]
    public async Task GetRewardsAsync_WithRewardIdFilter_ReturnsFilteredRewards()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardListRequest { Page = 1, PageSize = 10, RewardId = "reward_001" };

        // Act
        var result = await _rewardService.GetRewardsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(1, result.Total);
        Assert.Equal("reward_001", result.List[0].RewardId);
    }

    [Fact]
    public async Task GetRewardsAsync_WithPagination_ReturnsCorrectPage()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardListRequest { Page = 1, PageSize = 2 };

        // Act
        var result = await _rewardService.GetRewardsAsync(request);

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

    #endregion

    #region GetRewardByIdAsync Tests

    [Fact]
    public async Task GetRewardByIdAsync_WithExistingId_ReturnsReward()
    {
        // Arrange
        await SeedTestRewards();

        // Act
        var result = await _rewardService.GetRewardByIdAsync(1);

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

    [Fact]
    public async Task GetRewardByIdAsync_WithNonExistingId_ReturnsNull()
    {
        // Arrange
        await SeedTestRewards();

        // Act
        var result = await _rewardService.GetRewardByIdAsync(999);

        // Assert
        Assert.Null(result);
    }

    #endregion


    #region GetRewardsByRewardIdAsync Tests

    [Fact]
    public async Task GetRewardsByRewardIdAsync_WithExistingRewardId_ReturnsRewards()
    {
        // Arrange
        await SeedTestRewards();

        // Act
        var result = await _rewardService.GetRewardsByRewardIdAsync("reward_001");

        // Assert
        Assert.NotNull(result);
        Assert.Single(result);
        Assert.Equal("reward_001", result[0].RewardId);
    }

    [Fact]
    public async Task GetRewardsByRewardIdAsync_WithNonExistingRewardId_ReturnsEmptyList()
    {
        // Arrange
        await SeedTestRewards();

        // Act
        var result = await _rewardService.GetRewardsByRewardIdAsync("non_existing");

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

    [Fact]
    public async Task GetRewardsByRewardIdAsync_WithEmptyRewardId_ReturnsEmptyList()
    {
        // Arrange
        await SeedTestRewards();

        // Act
        var result = await _rewardService.GetRewardsByRewardIdAsync("");

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

    #endregion

    #region CreateRewardAsync Tests

    [Fact]
    public async Task CreateRewardAsync_WithValidDiamondReward_CreatesReward()
    {
        // Arrange
        var request = new RewardCreateRequest
        {
            RewardType = 1,
            RewardValue = 100,
            Description = "测试钻石奖励"
        };

        // Act
        var id = await _rewardService.CreateRewardAsync(request);

        // Assert
        Assert.True(id > 0);
        var reward = await _dbContext.Rewards.FindAsync(id);
        Assert.NotNull(reward);
        Assert.Equal(1, reward.RewardType);
        Assert.Equal(100, reward.RewardValue);
    }

    [Fact]
    public async Task CreateRewardAsync_WithValidCouponReward_CreatesReward()
    {
        // Arrange
        var request = new RewardCreateRequest
        {
            RewardType = 4,
            RewardExtend = 1,
            RewardValue = 0,
            Description = "测试优惠券奖励"
        };

        // Act
        var id = await _rewardService.CreateRewardAsync(request);

        // Assert
        Assert.True(id > 0);
        var reward = await _dbContext.Rewards.FindAsync(id);
        Assert.NotNull(reward);
        Assert.Equal(4, reward.RewardType);
        Assert.Equal(1, reward.RewardExtend);
    }

    [Fact]
    public async Task CreateRewardAsync_WithInvalidType_ThrowsException()
    {
        // Arrange
        var request = new RewardCreateRequest
        {
            RewardType = 5,
            RewardValue = 100
        };

        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _rewardService.CreateRewardAsync(request));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
    }

    [Fact]
    public async Task CreateRewardAsync_WithCouponTypeButNoCouponId_ThrowsException()
    {
        // Arrange
        var request = new RewardCreateRequest
        {
            RewardType = 4,
            RewardValue = 0
        };

        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _rewardService.CreateRewardAsync(request));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
    }

    [Fact]
    public async Task CreateRewardAsync_WithNonCouponTypeButZeroValue_ThrowsException()
    {
        // Arrange
        var request = new RewardCreateRequest
        {
            RewardType = 1,
            RewardValue = 0
        };

        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _rewardService.CreateRewardAsync(request));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
    }

    #endregion

    #region UpdateRewardAsync Tests

    [Fact]
    public async Task UpdateRewardAsync_WithExistingReward_UpdatesReward()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardUpdateRequest
        {
            RewardType = 1,
            RewardValue = 200,
            Description = "更新后的描述"
        };

        // Act
        var result = await _rewardService.UpdateRewardAsync(1, request);

        // Assert
        Assert.True(result);
        var reward = await _dbContext.Rewards.FindAsync(1);
        Assert.NotNull(reward);
        Assert.Equal(200, reward.RewardValue);
        Assert.Equal("更新后的描述", reward.Description);
    }

    [Fact]
    public async Task UpdateRewardAsync_WithNonExistingReward_ThrowsException()
    {
        // Arrange
        var request = new RewardUpdateRequest
        {
            RewardType = 1,
            RewardValue = 100
        };

        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _rewardService.UpdateRewardAsync(999, request));
        Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
    }

    #endregion

    #region DeleteRewardAsync Tests

    [Fact]
    public async Task DeleteRewardAsync_WithExistingReward_DeletesReward()
    {
        // Arrange
        await SeedTestRewards();

        // Act
        var result = await _rewardService.DeleteRewardAsync(1);

        // Assert
        Assert.True(result);
        var reward = await _dbContext.Rewards.FindAsync(1);
        Assert.Null(reward);
    }

    [Fact]
    public async Task DeleteRewardAsync_WithNonExistingReward_ThrowsException()
    {
        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _rewardService.DeleteRewardAsync(999));
        Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
    }

    #endregion


    #region BatchUpdateRewardsAsync Tests

    [Fact]
    public async Task BatchUpdateRewardsAsync_WithValidRequest_UpdatesRewards()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardBatchRequest
        {
            RewardId = "reward_001",
            Rewards = new List<RewardItem>
            {
                new() { RewardType = 1, RewardValue = 50, Description = "新钻石奖励" },
                new() { RewardType = 2, RewardValue = 100, Description = "新UU币奖励" }
            }
        };

        // Act
        var result = await _rewardService.BatchUpdateRewardsAsync(request);

        // Assert
        Assert.True(result);
        var rewards = await _dbContext.Rewards.Where(r => r.RewardId == "reward_001").ToListAsync();
        Assert.Equal(2, rewards.Count);
    }

    [Fact]
    public async Task BatchUpdateRewardsAsync_WithEmptyRewardId_ThrowsException()
    {
        // Arrange
        var request = new RewardBatchRequest
        {
            RewardId = "",
            Rewards = new List<RewardItem>()
        };

        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _rewardService.BatchUpdateRewardsAsync(request));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
    }

    [Fact]
    public async Task BatchUpdateRewardsAsync_WithEmptyRewardsList_DeletesExistingRewards()
    {
        // Arrange
        await SeedTestRewards();
        var request = new RewardBatchRequest
        {
            RewardId = "reward_001",
            Rewards = new List<RewardItem>()
        };

        // Act
        var result = await _rewardService.BatchUpdateRewardsAsync(request);

        // Assert
        Assert.True(result);
        var rewards = await _dbContext.Rewards.Where(r => r.RewardId == "reward_001").ToListAsync();
        Assert.Empty(rewards);
    }

    #endregion

    #region Helper Methods

    private async Task SeedTestRewards()
    {
        var rewards = new List<Reward>
        {
            new()
            {
                Id = 1,
                RewardId = "reward_001",
                RewardType = 1,
                RewardValue = 100,
                Description = "钻石奖励",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Id = 2,
                RewardId = "reward_002",
                RewardType = 2,
                RewardValue = 50,
                Description = "UU币奖励",
                CreatedAt = DateTime.Now.AddMinutes(-1),
                UpdatedAt = DateTime.Now.AddMinutes(-1)
            },
            new()
            {
                Id = 3,
                RewardId = "reward_003",
                RewardType = 3,
                RewardValue = 30,
                Description = "达达卷奖励",
                CreatedAt = DateTime.Now.AddMinutes(-2),
                UpdatedAt = DateTime.Now.AddMinutes(-2)
            }
        };

        _dbContext.Rewards.AddRange(rewards);
        await _dbContext.SaveChangesAsync();
    }

    #endregion
}
