using Bit.Seeder.Data.Enums;
using Bit.Seeder.Options;
namespace Bit.Seeder.Data.Distributions;
///
/// Named density profiles for CLI usage. Size-independent — user controls entity counts
/// via -u, -g, -c, -l flags separately.
///
public static class DensityProfiles
{
///
/// Balanced mid-market org. Even split between direct and group-mediated access.
/// Archetype: Sterling Cooper / Wayne Enterprises.
///
public static DensityProfile Balanced { get; } = new()
{
MembershipShape = MembershipDistributionShape.PowerLaw,
MembershipSkew = 0.6,
CollectionFanOutMin = 1,
CollectionFanOutMax = 5,
FanOutShape = CollectionFanOutShape.PowerLaw,
EmptyGroupRate = 0.1,
DirectAccessRatio = 0.5,
PermissionDistribution = PermissionDistributions.MidMarket,
UserCollectionMin = 1,
UserCollectionMax = 10,
UserCollectionShape = CollectionFanOutShape.PowerLaw,
UserCollectionSkew = 0.5,
CipherSkew = CipherCollectionSkew.HeavyRight,
OrphanCipherRate = 0.08,
MultiCollectionRate = 0.20,
MaxCollectionsPerCipher = 3,
PersonalCipherDistribution = PersonalCipherDistributions.Realistic,
FolderDistribution = FolderCountDistributions.Realistic,
};
///
/// High permission density with steep power-law membership and enterprise read-heavy permissions.
/// Archetype: Tyrell Corp / Bluth Company.
///
public static DensityProfile HighPerm { get; } = new()
{
MembershipShape = MembershipDistributionShape.PowerLaw,
MembershipSkew = 0.8,
CollectionFanOutMin = 2,
CollectionFanOutMax = 8,
FanOutShape = CollectionFanOutShape.PowerLaw,
EmptyGroupRate = 0.2,
DirectAccessRatio = 0.6,
PermissionDistribution = PermissionDistributions.Enterprise,
UserCollectionMin = 1,
UserCollectionMax = 30,
UserCollectionShape = CollectionFanOutShape.PowerLaw,
UserCollectionSkew = 0.7,
CipherSkew = CipherCollectionSkew.HeavyRight,
OrphanCipherRate = 0.15,
MultiCollectionRate = 0.30,
MaxCollectionsPerCipher = 4,
PersonalCipherDistribution = PersonalCipherDistributions.Realistic,
FolderDistribution = FolderCountDistributions.Enterprise,
};
///
/// Mega-group with high collection count and write-heavy permissions.
/// Archetype: Umbrella Corp.
///
public static DensityProfile HighCollection { get; } = new()
{
MembershipShape = MembershipDistributionShape.MegaGroup,
MembershipSkew = 0.5,
CollectionFanOutMin = 1,
CollectionFanOutMax = 3,
FanOutShape = CollectionFanOutShape.FrontLoaded,
EmptyGroupRate = 0.0,
DirectAccessRatio = 0.9,
PermissionDistribution = PermissionDistributions.MidMarketWriteHeavy,
UserCollectionMin = 1,
UserCollectionMax = 15,
UserCollectionShape = CollectionFanOutShape.PowerLaw,
UserCollectionSkew = 0.6,
CipherSkew = CipherCollectionSkew.HeavyRight,
OrphanCipherRate = 0.20,
MultiCollectionRate = 0.25,
MaxCollectionsPerCipher = 3,
PersonalCipherDistribution = PersonalCipherDistributions.Realistic,
FolderDistribution = FolderCountDistributions.Realistic,
};
///
/// Extreme mega-group with all-direct access and very high orphan rate.
/// Archetype: Initech (Baker McKenzie production pattern).
///
public static DensityProfile Broad { get; } = new()
{
MembershipShape = MembershipDistributionShape.MegaGroup,
MembershipSkew = 0.95,
CollectionFanOutMin = 1,
CollectionFanOutMax = 2,
FanOutShape = CollectionFanOutShape.Uniform,
EmptyGroupRate = 0.0,
DirectAccessRatio = 1.0,
PermissionDistribution = PermissionDistributions.EnterpriseManageHeavy,
UserCollectionMin = 1,
UserCollectionMax = 20,
UserCollectionShape = CollectionFanOutShape.PowerLaw,
UserCollectionSkew = 0.5,
CipherSkew = CipherCollectionSkew.HeavyRight,
OrphanCipherRate = 0.85,
MultiCollectionRate = 0.15,
MaxCollectionsPerCipher = 3,
PersonalCipherDistribution = PersonalCipherDistributions.LightUsage,
FolderDistribution = FolderCountDistributions.Minimal,
};
///
/// Low-complexity family/starter org with uniform distributions and no orphans.
/// Archetype: Central Perk.
///
public static DensityProfile Minimal { get; } = new()
{
MembershipShape = MembershipDistributionShape.Uniform,
MembershipSkew = 0.0,
CollectionFanOutMin = 1,
CollectionFanOutMax = 2,
FanOutShape = CollectionFanOutShape.Uniform,
EmptyGroupRate = 0.0,
DirectAccessRatio = 0.8,
PermissionDistribution = PermissionDistributions.Family,
UserCollectionMin = 1,
UserCollectionMax = 3,
UserCollectionShape = CollectionFanOutShape.Uniform,
UserCollectionSkew = 0.0,
CipherSkew = CipherCollectionSkew.Uniform,
OrphanCipherRate = 0.0,
MultiCollectionRate = 0.20,
MaxCollectionsPerCipher = 2,
PersonalCipherDistribution = PersonalCipherDistributions.Realistic,
FolderDistribution = FolderCountDistributions.Realistic,
};
///
/// Almost all access via groups, very low direct access. Tests CollectionGroup-heavy code paths.
///
public static DensityProfile GroupHeavy { get; } = new()
{
MembershipShape = MembershipDistributionShape.PowerLaw,
MembershipSkew = 0.7,
CollectionFanOutMin = 2,
CollectionFanOutMax = 6,
FanOutShape = CollectionFanOutShape.PowerLaw,
EmptyGroupRate = 0.1,
DirectAccessRatio = 0.1,
PermissionDistribution = PermissionDistributions.MidMarketWriteHeavy,
UserCollectionMin = 1,
UserCollectionMax = 8,
UserCollectionShape = CollectionFanOutShape.PowerLaw,
UserCollectionSkew = 0.5,
CipherSkew = CipherCollectionSkew.HeavyRight,
OrphanCipherRate = 0.10,
MultiCollectionRate = 0.20,
MaxCollectionsPerCipher = 3,
PersonalCipherDistribution = PersonalCipherDistributions.Realistic,
FolderDistribution = FolderCountDistributions.Realistic,
};
///
/// Low access density — few groups per collection, few collections per user, high orphan rate.
/// Models orgs where most users have minimal access and most ciphers are unassigned.
///
public static DensityProfile Sparse { get; } = new()
{
MembershipShape = MembershipDistributionShape.PowerLaw,
MembershipSkew = 0.5,
CollectionFanOutMin = 1,
CollectionFanOutMax = 2,
FanOutShape = CollectionFanOutShape.Uniform,
EmptyGroupRate = 0.3,
DirectAccessRatio = 0.3,
PermissionDistribution = PermissionDistributions.Enterprise,
UserCollectionMin = 1,
UserCollectionMax = 3,
UserCollectionShape = CollectionFanOutShape.Uniform,
UserCollectionSkew = 0.0,
CipherSkew = CipherCollectionSkew.HeavyRight,
OrphanCipherRate = 0.30,
MultiCollectionRate = 0.10,
MaxCollectionsPerCipher = 2,
PersonalCipherDistribution = PersonalCipherDistributions.LightUsage,
FolderDistribution = FolderCountDistributions.Minimal,
};
///
/// Parses a profile name to a . Returns null for null/empty input.
///
public static DensityProfile? Parse(string? name)
{
if (string.IsNullOrEmpty(name))
{
return null;
}
return name.ToLowerInvariant() switch
{
"balanced" => Balanced,
"highperm" => HighPerm,
"highcollection" => HighCollection,
"broad" => Broad,
"minimal" => Minimal,
"groupheavy" => GroupHeavy,
"sparse" => Sparse,
_ => throw new ArgumentException(
$"Unknown density profile '{name}'. Use: balanced, highPerm, highCollection, broad, minimal, groupHeavy, or sparse")
};
}
}