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

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

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

        _qyLevelService = new QyLevelService(_dbContext, _mockLogger.Object);
    }

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

    #region GetQyLevelsAsync Tests

    [Fact]
    public async Task GetQyLevelsAsync_WithNoFilters_ReturnsAllLevels()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _qyLevelService.GetQyLevelsAsync(request);

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

    [Fact]
    public async Task GetQyLevelsAsync_WithKeywordFilter_ReturnsFilteredLevels()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelListRequest { Page = 1, PageSize = 10, Keyword = "青铜" };

        // Act
        var result = await _qyLevelService.GetQyLevelsAsync(request);

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

    [Fact]
    public async Task GetQyLevelsAsync_WithPagination_ReturnsCorrectPage()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelListRequest { Page = 1, PageSize = 2 };

        // Act
        var result = await _qyLevelService.GetQyLevelsAsync(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);
    }

    [Fact]
    public async Task GetQyLevelsAsync_ExcludesDeletedLevels()
    {
        // Arrange
        await SeedTestQyLevels();
        // Mark one level as deleted
        var level = await _dbContext.EquityLevels.FindAsync(1);
        level!.DeletedAt = DateTime.Now;
        await _dbContext.SaveChangesAsync();

        var request = new QyLevelListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _qyLevelService.GetQyLevelsAsync(request);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(2, result.Total);
    }

    #endregion

    #region GetQyLevelByIdAsync Tests

    [Fact]
    public async Task GetQyLevelByIdAsync_WithExistingId_ReturnsLevel()
    {
        // Arrange
        await SeedTestQyLevels();

        // Act
        var result = await _qyLevelService.GetQyLevelByIdAsync(1);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(1, result.Id);
        Assert.Equal("青铜会员", result.Title);
    }

    [Fact]
    public async Task GetQyLevelByIdAsync_WithNonExistingId_ReturnsNull()
    {
        // Arrange
        await SeedTestQyLevels();

        // Act
        var result = await _qyLevelService.GetQyLevelByIdAsync(999);

        // Assert
        Assert.Null(result);
    }

    [Fact]
    public async Task GetQyLevelByIdAsync_WithDeletedLevel_ReturnsNull()
    {
        // Arrange
        await SeedTestQyLevels();
        var level = await _dbContext.EquityLevels.FindAsync(1);
        level!.DeletedAt = DateTime.Now;
        await _dbContext.SaveChangesAsync();

        // Act
        var result = await _qyLevelService.GetQyLevelByIdAsync(1);

        // Assert
        Assert.Null(result);
    }

    #endregion


    #region UpdateQyLevelAsync Tests

    [Fact]
    public async Task UpdateQyLevelAsync_WithValidRequest_UpdatesLevel()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelUpdateRequest
        {
            Level = 1,
            Title = "更新后的青铜会员",
            RequiredPoints = 200
        };

        // Act
        var result = await _qyLevelService.UpdateQyLevelAsync(1, request);

        // Assert
        Assert.True(result);
        var level = await _dbContext.EquityLevels.FindAsync(1);
        Assert.NotNull(level);
        Assert.Equal("更新后的青铜会员", level.Title);
        Assert.Equal(200, level.Number);
    }

    [Fact]
    public async Task UpdateQyLevelAsync_WithNonExistingLevel_ThrowsException()
    {
        // Arrange
        var request = new QyLevelUpdateRequest
        {
            Level = 1,
            Title = "测试",
            RequiredPoints = 100
        };

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

    [Fact]
    public async Task UpdateQyLevelAsync_WithInvalidLevel_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelUpdateRequest
        {
            Level = 0,
            Title = "测试",
            RequiredPoints = 100
        };

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

    [Fact]
    public async Task UpdateQyLevelAsync_WithEmptyTitle_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelUpdateRequest
        {
            Level = 1,
            Title = "",
            RequiredPoints = 100
        };

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

    [Fact]
    public async Task UpdateQyLevelAsync_WithNegativeRequiredPoints_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelUpdateRequest
        {
            Level = 1,
            Title = "测试",
            RequiredPoints = -1
        };

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

    #endregion

    #region GetQyLevelPrizesAsync Tests

    [Fact]
    public async Task GetQyLevelPrizesAsync_WithNoFilters_ReturnsAllPrizes()
    {
        // Arrange
        await SeedTestQyLevelsWithPrizes();
        var request = new QyLevelPrizeListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _qyLevelService.GetQyLevelPrizesAsync(1, request);

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

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

        // Act
        var result = await _qyLevelService.GetQyLevelPrizesAsync(1, request);

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

    [Fact]
    public async Task GetQyLevelPrizesAsync_WithNonExistingLevel_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeListRequest { Page = 1, PageSize = 10 };

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

    [Fact]
    public async Task GetQyLevelPrizesAsync_ExcludesDeletedPrizes()
    {
        // Arrange
        await SeedTestQyLevelsWithPrizes();
        // Mark one prize as deleted
        var prize = await _dbContext.EquityLevelRewards.FindAsync(1);
        prize!.DeletedAt = DateTime.Now;
        await _dbContext.SaveChangesAsync();

        var request = new QyLevelPrizeListRequest { Page = 1, PageSize = 10 };

        // Act
        var result = await _qyLevelService.GetQyLevelPrizesAsync(1, request);

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

    #endregion


    #region CreateQyLevelPrizeAsync Tests

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithValidPhysicalPrize_CreatesPrize()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 2,
            Title = "测试实物奖品",
            Value = 100,
            ExchangePrice = 50,
            ReferencePrice = 120,
            Probability = 10.5m,
            Image = "http://example.com/image.jpg"
        };

        // Act
        var id = await _qyLevelService.CreateQyLevelPrizeAsync(1, request);

        // Assert
        Assert.True(id > 0);
        var prize = await _dbContext.EquityLevelRewards.FindAsync(id);
        Assert.NotNull(prize);
        Assert.Equal(2, prize.Type);
        Assert.Equal("测试实物奖品", prize.Title);
    }

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithValidCouponPrize_CreatesPrize()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 1,
            CouponId = 1,
            Quantity = 5
        };

        // Act
        var id = await _qyLevelService.CreateQyLevelPrizeAsync(1, request);

        // Assert
        Assert.True(id > 0);
        var prize = await _dbContext.EquityLevelRewards.FindAsync(id);
        Assert.NotNull(prize);
        Assert.Equal(1, prize.Type);
        Assert.Equal(1, prize.CouponId);
        Assert.Equal(5, prize.ZNum);
    }

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithNonExistingLevel_ThrowsException()
    {
        // Arrange
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 2,
            Title = "测试",
            Value = 100,
            ExchangePrice = 50,
            ReferencePrice = 120,
            Image = "http://example.com/image.jpg"
        };

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

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithInvalidType_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 3,
            Title = "测试"
        };

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

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithCouponTypeButNoCouponId_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 1,
            Quantity = 5
        };

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

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithPhysicalTypeButNoTitle_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 2,
            Value = 100,
            ExchangePrice = 50,
            ReferencePrice = 120,
            Image = "http://example.com/image.jpg"
        };

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

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithInvalidProbability_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 2,
            Title = "测试",
            Value = 100,
            ExchangePrice = 50,
            ReferencePrice = 120,
            Probability = 150, // Invalid: > 100
            Image = "http://example.com/image.jpg"
        };

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

    [Fact]
    public async Task CreateQyLevelPrizeAsync_WithTooManyDecimalPlaces_ThrowsException()
    {
        // Arrange
        await SeedTestQyLevels();
        var request = new QyLevelPrizeCreateRequest
        {
            Type = 2,
            Title = "测试",
            Value = 100,
            ExchangePrice = 50,
            ReferencePrice = 120,
            Probability = 10.123m, // Invalid: > 2 decimal places
            Image = "http://example.com/image.jpg"
        };

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

    #endregion

    #region UpdateQyLevelPrizeAsync Tests

    [Fact]
    public async Task UpdateQyLevelPrizeAsync_WithValidRequest_UpdatesPrize()
    {
        // Arrange
        await SeedTestQyLevelsWithPrizes();
        var request = new QyLevelPrizeUpdateRequest
        {
            Type = 2,
            Title = "更新后的奖品",
            Value = 200,
            ExchangePrice = 100,
            ReferencePrice = 250,
            Image = "http://example.com/new-image.jpg"
        };

        // Act
        var result = await _qyLevelService.UpdateQyLevelPrizeAsync(2, request);

        // Assert
        Assert.True(result);
        var prize = await _dbContext.EquityLevelRewards.FindAsync(2);
        Assert.NotNull(prize);
        Assert.Equal("更新后的奖品", prize.Title);
        Assert.Equal(200, prize.JiangPrice);
    }

    [Fact]
    public async Task UpdateQyLevelPrizeAsync_WithNonExistingPrize_ThrowsException()
    {
        // Arrange
        var request = new QyLevelPrizeUpdateRequest
        {
            Type = 2,
            Title = "测试",
            Value = 100,
            ExchangePrice = 50,
            ReferencePrice = 120,
            Image = "http://example.com/image.jpg"
        };

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

    #endregion

    #region DeleteQyLevelPrizeAsync Tests

    [Fact]
    public async Task DeleteQyLevelPrizeAsync_WithExistingPrize_SoftDeletesPrize()
    {
        // Arrange
        await SeedTestQyLevelsWithPrizes();

        // Act
        var result = await _qyLevelService.DeleteQyLevelPrizeAsync(1);

        // Assert
        Assert.True(result);
        var prize = await _dbContext.EquityLevelRewards.FindAsync(1);
        Assert.NotNull(prize);
        Assert.NotNull(prize.DeletedAt);
    }

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

    #endregion

    #region Helper Methods

    private async Task SeedTestQyLevels()
    {
        var levels = new List<EquityLevel>
        {
            new()
            {
                Id = 1,
                Level = 1,
                Title = "青铜会员",
                Number = 100,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Id = 2,
                Level = 2,
                Title = "白银会员",
                Number = 500,
                CreatedAt = DateTime.Now.AddMinutes(-1),
                UpdatedAt = DateTime.Now.AddMinutes(-1)
            },
            new()
            {
                Id = 3,
                Level = 3,
                Title = "黄金会员",
                Number = 1000,
                CreatedAt = DateTime.Now.AddMinutes(-2),
                UpdatedAt = DateTime.Now.AddMinutes(-2)
            }
        };

        _dbContext.EquityLevels.AddRange(levels);
        await _dbContext.SaveChangesAsync();
    }

    private async Task SeedTestQyLevelsWithPrizes()
    {
        await SeedTestQyLevels();

        var prizes = new List<EquityLevelReward>
        {
            new()
            {
                Id = 1,
                EquityLevelId = 1,
                Type = 1,
                Title = "优惠券奖品",
                CouponId = 1,
                ZNum = 5,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            },
            new()
            {
                Id = 2,
                EquityLevelId = 1,
                Type = 2,
                Title = "实物奖品",
                JiangPrice = 100,
                Money = 50,
                ScMoney = 120,
                Probability = 10.5m,
                ImgUrl = "http://example.com/image.jpg",
                CreatedAt = DateTime.Now.AddMinutes(-1),
                UpdatedAt = DateTime.Now.AddMinutes(-1)
            }
        };

        _dbContext.EquityLevelRewards.AddRange(prizes);
        await _dbContext.SaveChangesAsync();
    }

    #endregion
}
