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") }; } }