using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Tiku.Domain.Catalog; using Tiku.Domain.Content; using Tiku.Domain.Identity; using Tiku.Domain.QuestionBanks; using Tiku.Domain.Tenancy; namespace Tiku.Infrastructure.Persistence.Configurations; internal sealed class ContentAssetAccessEventConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureEntity("content_asset_access_events"); builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id }); builder.Property(entity => entity.ActorRole).HasSnakeCaseEnum(); builder.Property(entity => entity.AccessType).HasSnakeCaseEnum(); builder.Property(entity => entity.Visibility).HasMaxLength(50); builder.Property(entity => entity.AssetType).HasMaxLength(50); builder.Property(entity => entity.StorageProvider).HasMaxLength(50); builder.Property(entity => entity.Disposition).HasNullableSnakeCaseEnum(); builder.Property(entity => entity.SignatureMode).HasMaxLength(100); builder.Property(entity => entity.Result).HasSnakeCaseEnum(); builder.Property(entity => entity.DenyCode).HasMaxLength(100); builder.Property(entity => entity.IpAddress).HasMaxLength(100); builder.Property(entity => entity.UserAgent).HasMaxLength(1024); builder.Property(entity => entity.Metadata).IsJson("{}"); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); builder.HasIndex(entity => new { entity.TenantId, entity.AssetId, entity.CreatedAt }); builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.CreatedAt }) .HasFilter("user_id is not null"); builder.HasIndex(entity => new { entity.TenantId, entity.AccessType, entity.Result, entity.CreatedAt }); builder.ToTable(table => table.HasCheckConstraint("ck_content_asset_access_events_expiry", "expires_in_seconds is null or expires_in_seconds > 0")); builder.HasOne().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.AssetId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.SetNull); } } internal sealed class ContentAssetSecurityScanEventConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureEntity("content_asset_security_scan_events"); builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id }); builder.Property(entity => entity.Provider).HasMaxLength(100); builder.Property(entity => entity.ScanStatus).HasSnakeCaseEnum(); builder.Property(entity => entity.RiskLevel).HasSnakeCaseEnum(); builder.Property(entity => entity.IssueCodes) .HasColumnType("text[]") .HasDefaultValueSql("'{}'::text[]"); builder.Property(entity => entity.Details).IsJson("{}"); builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()"); builder.HasIndex(entity => new { entity.TenantId, entity.AssetId, entity.CreatedAt }); builder.HasIndex(entity => new { entity.TenantId, entity.ScanStatus, entity.RiskLevel, entity.CreatedAt }); builder.HasOne().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.AssetId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); } } internal sealed class QuestionBankGrantConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureEntity("question_bank_grants"); builder.ConfigureTimestamps(); builder.Property(entity => entity.GrantScope).HasSnakeCaseEnum(); builder.Property(entity => entity.AllowedPlanCodes) .HasColumnType("text[]") .HasDefaultValueSql("'{}'::text[]"); builder.Property(entity => entity.AllowedTenantIds) .HasColumnType("uuid[]") .HasDefaultValueSql("'{}'::uuid[]"); builder.Property(entity => entity.AllowedRegionIds) .HasColumnType("uuid[]") .HasDefaultValueSql("'{}'::uuid[]"); builder.Property(entity => entity.AllowedSubjectIds) .HasColumnType("uuid[]") .HasDefaultValueSql("'{}'::uuid[]"); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.Metadata).IsJson("{}"); builder.HasIndex(entity => new { entity.SourceQuestionBankId, entity.Status, entity.StartsAt, entity.ExpiresAt }); builder.HasIndex(entity => entity.AllowedTenantIds).HasMethod("gin"); builder.HasIndex(entity => entity.AllowedPlanCodes).HasMethod("gin"); builder.HasOne().WithMany() .HasForeignKey(entity => entity.SourceQuestionBankId) .OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.SetNull); builder.HasOne().WithMany().HasForeignKey(entity => entity.UpdatedBy).OnDelete(DeleteBehavior.SetNull); } } internal sealed class TenantQuestionBankAdoptionConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("tenant_question_bank_adoptions"); builder.ConfigureTimestamps(); builder.Property(entity => entity.AdoptionMode).HasSnakeCaseEnum(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.SyncStatus).HasSnakeCaseEnum(); builder.Property(entity => entity.SourceSnapshot).IsJson("{}"); builder.Property(entity => entity.Metadata).IsJson("{}"); builder.HasIndex(entity => new { entity.TenantId, entity.SourceQuestionBankId }).IsUnique(); builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.UpdatedAt }); builder.ToTable(table => table.HasCheckConstraint("ck_tenant_question_bank_adoptions_copied_count", "copied_question_count >= 0")); builder.HasOne().WithMany() .HasForeignKey(entity => entity.SourceQuestionBankId) .OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => entity.GrantId) .OnDelete(DeleteBehavior.SetNull); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.TargetQuestionBankId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.TargetEntryId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.TargetCollectionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); builder.HasOne().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.SetNull); builder.HasOne().WithMany().HasForeignKey(entity => entity.UpdatedBy).OnDelete(DeleteBehavior.SetNull); } } internal sealed class AiRecommendationReportConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ConfigureTenantEntity("ai_recommendation_reports"); builder.ConfigureTimestamps(); builder.Property(entity => entity.Status).HasSnakeCaseEnum(); builder.Property(entity => entity.Provider).HasMaxLength(100); builder.Property(entity => entity.Model).HasMaxLength(100); builder.Property(entity => entity.PromptVersion).HasMaxLength(100); builder.Property(entity => entity.InputPayload).IsJson("{}"); builder.Property(entity => entity.ContextPayload).IsJson("{}"); builder.Property(entity => entity.ResultPayload).IsJson("{}"); builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.CreatedAt }); builder.HasIndex(entity => new { entity.TenantId, entity.RegionId, entity.CreatedAt }); builder.HasOne().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade); builder.HasOne().WithMany() .HasForeignKey(entity => new { entity.TenantId, entity.RegionId }) .HasPrincipalKey(entity => new { entity.TenantId, entity.Id }) .OnDelete(DeleteBehavior.Restrict); } }