using FsCheck;
using FsCheck.Xunit;
using Microsoft.EntityFrameworkCore;
using MilitaryTrainingManagement.Authorization;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Models.Enums;
using MilitaryTrainingManagement.Services.Implementations;

namespace MilitaryTrainingManagement.Tests.Properties;

/// <summary>
/// 组织层级访问控制属性测试
/// **功能：military-training-material-management，属性1：组织层级访问控制**
/// **验证需求：需求1.3, 4.4, 8.1, 8.2, 8.4**
/// </summary>
public class OrganizationalAccessControlPropertyTests
{
    /// <summary>
    /// 创建内存数据库上下文
    /// </summary>
    private static ApplicationDbContext CreateInMemoryContext(string dbName)
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: dbName)
            .Options;
        return new ApplicationDbContext(options);
    }

    /// <summary>
    /// 创建测试用的组织层级结构
    /// </summary>
    private static Dictionary<OrganizationalLevel, OrganizationalUnit> CreateHierarchy(
        ApplicationDbContext context)
    {
        var division = new OrganizationalUnit
        {
            Name = "师团",
            Level = OrganizationalLevel.Division,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(division);
        context.SaveChanges();

        var regiment = new OrganizationalUnit
        {
            Name = "团",
            Level = OrganizationalLevel.Regiment,
            ParentId = division.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(regiment);
        context.SaveChanges();

        var battalion = new OrganizationalUnit
        {
            Name = "营",
            Level = OrganizationalLevel.Battalion,
            ParentId = regiment.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(battalion);
        context.SaveChanges();

        var company = new OrganizationalUnit
        {
            Name = "连",
            Level = OrganizationalLevel.Company,
            ParentId = battalion.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(company);
        context.SaveChanges();

        return new Dictionary<OrganizationalLevel, OrganizationalUnit>
        {
            { OrganizationalLevel.Division, division },
            { OrganizationalLevel.Regiment, regiment },
            { OrganizationalLevel.Battalion, battalion },
            { OrganizationalLevel.Company, company }
        };
    }

    /// <summary>
    /// 属性1：组织层级访问控制
    /// *对于任何*用户和任何数据资源，只有当用户的组织层级等于或高于资源的组织层级时，才应授予访问权限
    /// **验证需求：需求1.3, 4.4, 8.1, 8.2, 8.4**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool UserCanAccessResource_OnlyWhenLevelIsEqualOrHigher(
        OrganizationalLevel userLevel, 
        OrganizationalLevel resourceLevel)
    {
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var hierarchy = CreateHierarchy(context);
        var organizationService = new OrganizationService(context);
        var authService = new OrganizationalAuthorizationService(organizationService);

        var userUnit = hierarchy[userLevel];
        var resourceUnit = hierarchy[resourceLevel];

        // 检查用户是否可以访问资源
        var canAccess = authService.CanAccessUnitAsync(userUnit.Id, resourceUnit.Id)
            .GetAwaiter().GetResult();

        // 用户层级数值越小表示层级越高
        // 用户只能访问自己单位或下级单位的资源
        var userLevelValue = (int)userLevel;
        var resourceLevelValue = (int)resourceLevel;

        // 如果用户层级 <= 资源层级（用户层级更高或相等），且资源在用户的下级链中
        // 则应该可以访问
        if (userLevelValue <= resourceLevelValue)
        {
            // 用户层级更高或相等，应该可以访问（如果在同一层级链中）
            return canAccess == true;
        }
        else
        {
            // 用户层级更低，不应该可以访问上级资源
            return canAccess == false;
        }
    }

    /// <summary>
    /// 属性1扩展：用户始终可以访问自己单位的资源
    /// **验证需求：需求1.3, 8.1**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool UserCanAlwaysAccessOwnUnitResources(OrganizationalLevel userLevel)
    {
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var hierarchy = CreateHierarchy(context);
        var organizationService = new OrganizationService(context);
        var authService = new OrganizationalAuthorizationService(organizationService);

        var userUnit = hierarchy[userLevel];

        // 用户应该始终可以访问自己单位的资源
        var canAccess = authService.CanAccessUnitAsync(userUnit.Id, userUnit.Id)
            .GetAwaiter().GetResult();

        return canAccess == true;
    }

    /// <summary>
    /// 属性1扩展：HasMinimumLevel正确判断层级权限
    /// **验证需求：需求1.3**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool HasMinimumLevel_CorrectlyDeterminesAccess(
        OrganizationalLevel userLevel, 
        OrganizationalLevel minimumRequired)
    {
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var organizationService = new OrganizationService(context);
        var authService = new OrganizationalAuthorizationService(organizationService);

        var hasAccess = authService.HasMinimumLevel(userLevel, minimumRequired);

        // 用户层级数值 <= 要求的最低层级数值时，应该有权限
        var expected = (int)userLevel <= (int)minimumRequired;

        return hasAccess == expected;
    }

    /// <summary>
    /// 属性1扩展：下级单位不能访问上级单位资源
    /// **验证需求：需求8.2, 8.4**
    /// </summary>
    [Property(MaxTest = 100)]
    public bool SubordinateCannotAccessSuperiorResources(
        OrganizationalLevel userLevel, 
        OrganizationalLevel resourceLevel)
    {
        // 只测试用户层级低于资源层级的情况
        if ((int)userLevel <= (int)resourceLevel)
            return true; // 跳过这种情况，由其他测试覆盖

        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var hierarchy = CreateHierarchy(context);
        var organizationService = new OrganizationService(context);
        var authService = new OrganizationalAuthorizationService(organizationService);

        var userUnit = hierarchy[userLevel];
        var resourceUnit = hierarchy[resourceLevel];

        // 下级单位不应该能访问上级单位的资源
        var canAccess = authService.CanAccessUnitAsync(userUnit.Id, resourceUnit.Id)
            .GetAwaiter().GetResult();

        return canAccess == false;
    }
}
