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

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

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

        _signConfigService = new SignConfigService(_dbContext, _mockLogger.Object);
    }

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

    #region GetSignConfigsAsync Tests

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

        // Act
        var result = await _signConfigService.GetSignConfigsAsync(request);

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

    [Fact]
    public async Task GetSignConfigsAsync_WithCumulativeType_ReturnsCumulativeConfigs()
    {
        // Arrange
        await SeedTestSignConfigs();
        var request = new SignConfigListRequest { Page = 1, PageSize = 10, Type = 2 };

        // Act
        var result = await _signConfigService.GetSignConfigsAsync(request);

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


    [Fact]
    public async Task GetSignConfigsAsync_WithKeywordFilter_ReturnsFilteredConfigs()
    {
        // Arrange
        await SeedTestSignConfigs();
        var request = new SignConfigListRequest { Page = 1, PageSize = 10, Type = 1, Keyword = "第1天" };

        // Act
        var result = await _signConfigService.GetSignConfigsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(1, result.Total);
        Assert.Contains("第1天", result.List[0].Title);
    }

    [Fact]
    public async Task GetSignConfigsAsync_WithPagination_ReturnsCorrectPage()
    {
        // Arrange
        await SeedTestSignConfigs();
        var request = new SignConfigListRequest { Page = 1, PageSize = 1, Type = 1 };

        // Act
        var result = await _signConfigService.GetSignConfigsAsync(request);

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

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

        // Act
        var result = await _signConfigService.GetSignConfigsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.True(result.List.Count > 0);
        Assert.True(result.List[0].Rewards.Count > 0);
    }

    #endregion

    #region GetSignConfigByIdAsync Tests

    [Fact]
    public async Task GetSignConfigByIdAsync_WithExistingId_ReturnsConfig()
    {
        // Arrange
        await SeedTestSignConfigs();

        // Act
        var result = await _signConfigService.GetSignConfigByIdAsync(1);

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

    [Fact]
    public async Task GetSignConfigByIdAsync_WithNonExistingId_ReturnsNull()
    {
        // Arrange
        await SeedTestSignConfigs();

        // Act
        var result = await _signConfigService.GetSignConfigByIdAsync(999);

        // Assert
        Assert.Null(result);
    }

    #endregion

    #region CreateSignConfigAsync Tests

    [Fact]
    public async Task CreateSignConfigAsync_WithValidRequest_CreatesConfig()
    {
        // Arrange
        var request = new SignConfigCreateRequest
        {
            Type = 1,
            Day = 1,
            Title = "测试签到配置",
            Icon = "https://example.com/icon.png",
            Sort = 1,
            Description = "测试描述",
            Rewards = new List<RewardItem>
            {
                new() { RewardType = 1, RewardValue = 100, Description = "钻石奖励" }
            }
        };

        // Act
        var id = await _signConfigService.CreateSignConfigAsync(request);

        // Assert
        Assert.True(id > 0);
        var config = await _dbContext.SignConfigs.FindAsync(id);
        Assert.NotNull(config);
        Assert.Equal((byte)1, config.Type);
        Assert.Equal((byte)1, config.Day);
        Assert.Equal("测试签到配置", config.Title);
    }

    [Fact]
    public async Task CreateSignConfigAsync_WithInvalidType_ThrowsException()
    {
        // Arrange
        var request = new SignConfigCreateRequest
        {
            Type = 3,
            Day = 1,
            Title = "测试签到配置"
        };

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

    [Fact]
    public async Task CreateSignConfigAsync_WithEmptyTitle_ThrowsException()
    {
        // Arrange
        var request = new SignConfigCreateRequest
        {
            Type = 1,
            Day = 1,
            Title = ""
        };

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

    [Fact]
    public async Task CreateSignConfigAsync_WithZeroDay_ThrowsException()
    {
        // Arrange
        var request = new SignConfigCreateRequest
        {
            Type = 1,
            Day = 0,
            Title = "测试签到配置"
        };

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

    #endregion

    #region UpdateSignConfigAsync Tests

    [Fact]
    public async Task UpdateSignConfigAsync_WithExistingConfig_UpdatesConfig()
    {
        // Arrange
        await SeedTestSignConfigs();
        var request = new SignConfigUpdateRequest
        {
            Type = 1,
            Day = 2,
            Title = "更新后的标题",
            Icon = "https://example.com/new-icon.png",
            Sort = 10,
            Description = "更新后的描述"
        };

        // Act
        var result = await _signConfigService.UpdateSignConfigAsync(1, request);

        // Assert
        Assert.True(result);
        var config = await _dbContext.SignConfigs.FindAsync(1);
        Assert.NotNull(config);
        Assert.Equal((byte)2, config.Day);
        Assert.Equal("更新后的标题", config.Title);
    }

    [Fact]
    public async Task UpdateSignConfigAsync_WithNonExistingConfig_ThrowsException()
    {
        // Arrange
        var request = new SignConfigUpdateRequest
        {
            Type = 1,
            Day = 1,
            Title = "测试"
        };

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

    #endregion

    #region DeleteSignConfigAsync Tests

    [Fact]
    public async Task DeleteSignConfigAsync_WithExistingConfig_DeletesConfig()
    {
        // Arrange
        await SeedTestSignConfigs();

        // Act
        var result = await _signConfigService.DeleteSignConfigAsync(1);

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

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

    [Fact]
    public async Task DeleteSignConfigAsync_WithRewards_DeletesConfigAndRewards()
    {
        // Arrange
        await SeedTestSignConfigsWithRewards();
        var configBefore = await _dbContext.SignConfigs.FindAsync(1);
        var rewardsBefore = await _dbContext.Rewards.Where(r => r.RewardId == configBefore!.RewardId).ToListAsync();
        Assert.NotEmpty(rewardsBefore);

        // Act
        var result = await _signConfigService.DeleteSignConfigAsync(1);

        // Assert
        Assert.True(result);
        var config = await _dbContext.SignConfigs.FindAsync(1);
        Assert.Null(config);
        var rewardsAfter = await _dbContext.Rewards.Where(r => r.RewardId == configBefore!.RewardId).ToListAsync();
        Assert.Empty(rewardsAfter);
    }

    #endregion


    #region UpdateSignConfigStatusAsync Tests

    [Fact]
    public async Task UpdateSignConfigStatusAsync_WithValidStatus_UpdatesStatus()
    {
        // Arrange
        await SeedTestSignConfigs();

        // Act
        var result = await _signConfigService.UpdateSignConfigStatusAsync(1, 0);

        // Assert
        Assert.True(result);
        var config = await _dbContext.SignConfigs.FindAsync(1);
        Assert.NotNull(config);
        Assert.Equal(0, config.Status);
    }

    [Fact]
    public async Task UpdateSignConfigStatusAsync_WithInvalidStatus_ThrowsException()
    {
        // Arrange
        await SeedTestSignConfigs();

        // Act & Assert
        var ex = await Assert.ThrowsAsync<BusinessException>(
            () => _signConfigService.UpdateSignConfigStatusAsync(1, 2));
        Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
    }

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

    #endregion

    #region UpdateSignConfigSortAsync Tests

    [Fact]
    public async Task UpdateSignConfigSortAsync_WithValidSort_UpdatesSort()
    {
        // Arrange
        await SeedTestSignConfigs();

        // Act
        var result = await _signConfigService.UpdateSignConfigSortAsync(1, 100);

        // Assert
        Assert.True(result);
        var config = await _dbContext.SignConfigs.FindAsync(1);
        Assert.NotNull(config);
        Assert.Equal(100, config.Sort);
    }

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

    #endregion

    #region UpdateSignConfigRewardAsync Tests

    [Fact]
    public async Task UpdateSignConfigRewardAsync_WithValidRequest_UpdatesRewards()
    {
        // Arrange
        await SeedTestSignConfigsWithRewards();
        var request = new SignConfigRewardRequest
        {
            Rewards = new List<RewardItem>
            {
                new() { RewardType = 1, RewardValue = 200, Description = "新钻石奖励" },
                new() { RewardType = 2, RewardValue = 100, Description = "新UU币奖励" }
            }
        };

        // Act
        var result = await _signConfigService.UpdateSignConfigRewardAsync(1, request);

        // Assert
        Assert.True(result);
        var config = await _dbContext.SignConfigs.FindAsync(1);
        var rewards = await _dbContext.Rewards.Where(r => r.RewardId == config!.RewardId).ToListAsync();
        Assert.Equal(2, rewards.Count);
    }

    [Fact]
    public async Task UpdateSignConfigRewardAsync_WithEmptyRewards_DeletesExistingRewards()
    {
        // Arrange
        await SeedTestSignConfigsWithRewards();
        var request = new SignConfigRewardRequest
        {
            Rewards = new List<RewardItem>()
        };

        // Act
        var result = await _signConfigService.UpdateSignConfigRewardAsync(1, request);

        // Assert
        Assert.True(result);
        var config = await _dbContext.SignConfigs.FindAsync(1);
        var rewards = await _dbContext.Rewards.Where(r => r.RewardId == config!.RewardId).ToListAsync();
        Assert.Empty(rewards);
    }

    [Fact]
    public async Task UpdateSignConfigRewardAsync_WithNonExistingConfig_ThrowsException()
    {
        // Arrange
        var request = new SignConfigRewardRequest
        {
            Rewards = new List<RewardItem>()
        };

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

    #endregion

    #region Helper Methods

    private async Task SeedTestSignConfigs()
    {
        var configs = new List<SignConfig>
        {
            new()
            {
                Id = 1,
                Type = 1,
                Day = 1,
                Title = "第1天签到",
                Icon = "https://example.com/icon1.png",
                Status = 1,
                Sort = 1,
                RewardId = "sign_001",
                Description = "第1天签到奖励",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Id = 2,
                Type = 1,
                Day = 2,
                Title = "第2天签到",
                Icon = "https://example.com/icon2.png",
                Status = 1,
                Sort = 2,
                RewardId = "sign_002",
                Description = "第2天签到奖励",
                CreatedAt = DateTime.Now.AddMinutes(-1),
                UpdatedAt = DateTime.Now.AddMinutes(-1)
            },
            new()
            {
                Id = 3,
                Type = 2,
                Day = 7,
                Title = "累计7天签到",
                Icon = "https://example.com/icon3.png",
                Status = 1,
                Sort = 1,
                RewardId = "sign_003",
                Description = "累计7天签到奖励",
                CreatedAt = DateTime.Now.AddMinutes(-2),
                UpdatedAt = DateTime.Now.AddMinutes(-2)
            }
        };

        _dbContext.SignConfigs.AddRange(configs);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedTestSignConfigsWithRewards()
    {
        var configs = new List<SignConfig>
        {
            new()
            {
                Id = 1,
                Type = 1,
                Day = 1,
                Title = "第1天签到",
                Icon = "https://example.com/icon1.png",
                Status = 1,
                Sort = 1,
                RewardId = "sign_reward_001",
                Description = "第1天签到奖励",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

        var rewards = new List<Reward>
        {
            new()
            {
                Id = 100,
                RewardId = "sign_reward_001",
                RewardType = 1,
                RewardValue = 100,
                Description = "钻石奖励",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Id = 101,
                RewardId = "sign_reward_001",
                RewardType = 2,
                RewardValue = 50,
                Description = "UU币奖励",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            }
        };

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

    #endregion
}
