using FsCheck;
using FsCheck.Xunit;
using MilitaryTrainingManagement.Models.Entities;

namespace MilitaryTrainingManagement.Tests.Properties;

/// <summary>
/// 配额验证属性测试
/// **功能：military-training-material-management，属性2：配额总和验证**
/// **验证需求：需求2.3**
/// </summary>
public class AllocationQuotaPropertyTests
{
    /// <summary>
    /// 属性2：配额总和验证
    /// *对于任何*带有单位配额的物资配额，所有单位配额的总和不应超过总配额指标
    /// **验证需求：需求2.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool ValidDistribution_SumDoesNotExceedTotalQuota_ValidationPasses(
        PositiveInt totalQuotaInt,
        PositiveInt quota1Int,
        PositiveInt quota2Int,
        PositiveInt quota3Int)
    {
        var totalQuota = (decimal)totalQuotaInt.Get * 100; // 确保总配额足够大
        var quota1 = (decimal)(quota1Int.Get % 30 + 1);
        var quota2 = (decimal)(quota2Int.Get % 30 + 1);
        var quota3 = (decimal)(quota3Int.Get % 30 + 1);

        // 确保总和不超过总配额
        var sum = quota1 + quota2 + quota3;
        if (sum > totalQuota)
        {
            // 调整配额使其有效
            var scale = totalQuota / sum * 0.9m;
            quota1 = Math.Floor(quota1 * scale);
            quota2 = Math.Floor(quota2 * scale);
            quota3 = Math.Floor(quota3 * scale);
        }

        var allocation = CreateAllocationWithDistributions(totalQuota, new[] { quota1, quota2, quota3 });

        // 验证：当总和不超过总配额时，验证应该通过
        var actualSum = allocation.Distributions.Sum(d => d.UnitQuota);
        var isValid = allocation.ValidateDistributionQuotas();

        return actualSum <= totalQuota && isValid == true;
    }

    /// <summary>
    /// 属性2扩展：无效分配（总和超过总配额）应该被拒绝
    /// **验证需求：需求2.3, 2.5**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool InvalidDistribution_SumExceedsTotalQuota_ValidationFails(
        PositiveInt totalQuotaInt,
        PositiveInt extraInt)
    {
        var totalQuota = (decimal)totalQuotaInt.Get;
        var extra = (decimal)(extraInt.Get % 100 + 1); // 确保超出部分至少为1

        // 创建总和超过总配额的分配
        var quota1 = totalQuota * 0.6m;
        var quota2 = totalQuota * 0.5m + extra; // 确保总和超过总配额

        var allocation = CreateAllocationWithDistributions(totalQuota, new[] { quota1, quota2 });

        // 验证：当总和超过总配额时，验证应该失败
        var actualSum = allocation.Distributions.Sum(d => d.UnitQuota);
        var isValid = allocation.ValidateDistributionQuotas();

        return actualSum > totalQuota && isValid == false;
    }

    /// <summary>
    /// 属性2扩展：空分配列表应该是有效的
    /// **验证需求：需求2.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool EmptyDistribution_ShouldBeValid(PositiveInt totalQuotaInt)
    {
        var totalQuota = (decimal)totalQuotaInt.Get;

        var allocation = new MaterialAllocation
        {
            Id = 1,
            Category = "测试类别",
            MaterialName = "测试物资",
            Unit = "件",
            TotalQuota = totalQuota,
            CreatedByUnitId = 1,
            CreatedAt = DateTime.UtcNow
        };

        // 空分配列表应该是有效的
        return allocation.ValidateDistributionQuotas() == true;
    }

    /// <summary>
    /// 属性2扩展：单个分配等于总配额应该是有效的
    /// **验证需求：需求2.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool SingleDistribution_EqualToTotalQuota_ShouldBeValid(PositiveInt totalQuotaInt)
    {
        var totalQuota = (decimal)totalQuotaInt.Get;

        var allocation = CreateAllocationWithDistributions(totalQuota, new[] { totalQuota });

        // 单个分配等于总配额应该是有效的
        return allocation.ValidateDistributionQuotas() == true;
    }

    /// <summary>
    /// 属性2扩展：分配配额总和等于总配额应该是有效的（边界情况）
    /// **验证需求：需求2.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool Distribution_SumEqualsTotal_ShouldBeValid(PositiveInt totalQuotaInt, PositiveInt countInt)
    {
        var totalQuota = (decimal)totalQuotaInt.Get;
        var count = Math.Max(1, countInt.Get % 10 + 1); // 1-10个分配

        // 平均分配，确保总和等于总配额
        var quotaPerUnit = Math.Floor(totalQuota / count);
        var remainder = totalQuota - (quotaPerUnit * count);

        var quotas = new decimal[count];
        for (int i = 0; i < count; i++)
        {
            quotas[i] = quotaPerUnit;
            if (i == count - 1)
            {
                quotas[i] += remainder; // 最后一个单位获得余数
            }
        }

        var allocation = CreateAllocationWithDistributions(totalQuota, quotas);

        // 总和等于总配额应该是有效的
        var sum = allocation.Distributions.Sum(d => d.UnitQuota);
        return sum == totalQuota && allocation.ValidateDistributionQuotas() == true;
    }

    /// <summary>
    /// 属性2扩展：分配配额总和略小于总配额应该是有效的
    /// **验证需求：需求2.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool Distribution_SumLessThanTotal_ShouldBeValid(
        PositiveInt totalQuotaInt,
        PositiveInt marginInt)
    {
        var totalQuota = (decimal)(totalQuotaInt.Get + 100); // 确保总配额足够大
        var margin = (decimal)(marginInt.Get % 50 + 1); // 1-50的余量

        // 创建总和小于总配额的分配
        var quota1 = (totalQuota - margin) * 0.5m;
        var quota2 = (totalQuota - margin) * 0.5m;

        var allocation = CreateAllocationWithDistributions(totalQuota, new[] { quota1, quota2 });

        // 总和小于总配额应该是有效的
        var sum = allocation.Distributions.Sum(d => d.UnitQuota);
        return sum < totalQuota && allocation.ValidateDistributionQuotas() == true;
    }

    /// <summary>
    /// 创建带有分配的物资配额
    /// </summary>
    private static MaterialAllocation CreateAllocationWithDistributions(decimal totalQuota, decimal[] quotas)
    {
        var allocation = new MaterialAllocation
        {
            Id = 1,
            Category = "测试类别",
            MaterialName = "测试物资",
            Unit = "件",
            TotalQuota = totalQuota,
            CreatedByUnitId = 1,
            CreatedAt = DateTime.UtcNow
        };

        var targetUnitId = 100;
        foreach (var quota in quotas)
        {
            allocation.Distributions.Add(new AllocationDistribution
            {
                AllocationId = allocation.Id,
                TargetUnitId = targetUnitId++,
                UnitQuota = quota
            });
        }

        return allocation;
    }
}
