using Microsoft.EntityFrameworkCore; using LittleShop.Models; namespace LittleShop.Data; public class LittleShopContext : DbContext { public LittleShopContext(DbContextOptions options) : base(options) { } public DbSet Users { get; set; } public DbSet Categories { get; set; } public DbSet Products { get; set; } public DbSet ProductPhotos { get; set; } public DbSet ProductVariations { get; set; } public DbSet Orders { get; set; } public DbSet OrderItems { get; set; } public DbSet CryptoPayments { get; set; } public DbSet ShippingRates { get; set; } public DbSet Bots { get; set; } public DbSet BotMetrics { get; set; } public DbSet BotSessions { get; set; } public DbSet Customers { get; set; } public DbSet CustomerMessages { get; set; } public DbSet PushSubscriptions { get; set; } public DbSet Reviews { get; set; } public DbSet BotContacts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // User entity modelBuilder.Entity(entity => { entity.HasIndex(e => e.Username).IsUnique(); }); // Category entity modelBuilder.Entity(entity => { entity.HasMany(c => c.Products) .WithOne(p => p.Category) .HasForeignKey(p => p.CategoryId) .OnDelete(DeleteBehavior.Restrict); }); // Product entity modelBuilder.Entity(entity => { entity.HasMany(p => p.Photos) .WithOne(pp => pp.Product) .HasForeignKey(pp => pp.ProductId) .OnDelete(DeleteBehavior.Cascade); entity.HasMany(p => p.Variations) .WithOne(pv => pv.Product) .HasForeignKey(pv => pv.ProductId) .OnDelete(DeleteBehavior.Cascade); entity.HasMany(p => p.OrderItems) .WithOne(oi => oi.Product) .HasForeignKey(oi => oi.ProductId) .OnDelete(DeleteBehavior.Restrict); }); // ProductVariation entity modelBuilder.Entity(entity => { entity.HasIndex(e => new { e.ProductId, e.Quantity }).IsUnique(); // One variation per quantity per product entity.HasIndex(e => new { e.ProductId, e.SortOrder }); entity.HasIndex(e => e.IsActive); entity.HasMany(pv => pv.OrderItems) .WithOne(oi => oi.ProductVariation) .HasForeignKey(oi => oi.ProductVariationId) .OnDelete(DeleteBehavior.Restrict); }); // Order entity modelBuilder.Entity(entity => { entity.HasOne(o => o.Customer) .WithMany(c => c.Orders) .HasForeignKey(o => o.CustomerId) .OnDelete(DeleteBehavior.Restrict) .IsRequired(false); // Make customer optional for transition entity.HasMany(o => o.Items) .WithOne(oi => oi.Order) .HasForeignKey(oi => oi.OrderId) .OnDelete(DeleteBehavior.Cascade); entity.HasMany(o => o.Payments) .WithOne(cp => cp.Order) .HasForeignKey(cp => cp.OrderId) .OnDelete(DeleteBehavior.Cascade); entity.HasIndex(e => e.CustomerId); entity.HasIndex(e => e.IdentityReference); entity.HasIndex(e => e.CreatedAt); }); // OrderItem entity modelBuilder.Entity(entity => { entity.HasKey(oi => oi.Id); }); // CryptoPayment entity modelBuilder.Entity(entity => { entity.HasIndex(e => e.BTCPayInvoiceId); entity.HasIndex(e => e.WalletAddress); }); // Bot entity modelBuilder.Entity(entity => { entity.HasIndex(e => e.BotKey).IsUnique(); entity.HasIndex(e => e.Name); entity.HasIndex(e => e.Status); entity.HasMany(b => b.Metrics) .WithOne(m => m.Bot) .HasForeignKey(m => m.BotId) .OnDelete(DeleteBehavior.Cascade); entity.HasMany(b => b.Sessions) .WithOne(s => s.Bot) .HasForeignKey(s => s.BotId) .OnDelete(DeleteBehavior.Cascade); }); // BotMetric entity modelBuilder.Entity(entity => { entity.HasIndex(e => new { e.BotId, e.RecordedAt }); entity.HasIndex(e => e.MetricType); }); // BotSession entity modelBuilder.Entity(entity => { entity.HasIndex(e => new { e.BotId, e.SessionIdentifier }); entity.HasIndex(e => e.StartedAt); entity.HasIndex(e => e.LastActivityAt); }); // Customer entity modelBuilder.Entity(entity => { entity.HasIndex(e => e.TelegramUserId).IsUnique(); entity.HasIndex(e => e.TelegramUsername); entity.HasIndex(e => e.Email); entity.HasIndex(e => e.CreatedAt); entity.HasIndex(e => e.LastActiveAt); entity.HasIndex(e => e.DataRetentionDate); entity.HasMany(c => c.Orders) .WithOne(o => o.Customer) .HasForeignKey(o => o.CustomerId) .OnDelete(DeleteBehavior.Restrict); entity.HasMany(c => c.Messages) .WithOne(m => m.Customer) .HasForeignKey(m => m.CustomerId) .OnDelete(DeleteBehavior.Cascade); }); // CustomerMessage entity modelBuilder.Entity(entity => { entity.HasOne(m => m.Customer) .WithMany(c => c.Messages) .HasForeignKey(m => m.CustomerId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(m => m.Order) .WithMany() .HasForeignKey(m => m.OrderId) .OnDelete(DeleteBehavior.SetNull); entity.HasOne(m => m.AdminUser) .WithMany() .HasForeignKey(m => m.AdminUserId) .OnDelete(DeleteBehavior.SetNull); entity.HasOne(m => m.ParentMessage) .WithMany(m => m.Replies) .HasForeignKey(m => m.ParentMessageId) .OnDelete(DeleteBehavior.Restrict); entity.HasIndex(e => new { e.CustomerId, e.CreatedAt }); entity.HasIndex(e => e.OrderId); entity.HasIndex(e => e.Status); entity.HasIndex(e => e.Direction); entity.HasIndex(e => e.Type); entity.HasIndex(e => e.ScheduledFor); entity.HasIndex(e => e.ThreadId); entity.HasIndex(e => e.ParentMessageId); }); // PushSubscription entity modelBuilder.Entity(entity => { entity.HasOne(ps => ps.User) .WithMany() .HasForeignKey(ps => ps.UserId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(ps => ps.Customer) .WithMany() .HasForeignKey(ps => ps.CustomerId) .OnDelete(DeleteBehavior.Cascade); entity.HasIndex(e => e.Endpoint).IsUnique(); entity.HasIndex(e => e.UserId); entity.HasIndex(e => e.CustomerId); entity.HasIndex(e => e.SubscribedAt); entity.HasIndex(e => e.IsActive); }); // Review entity modelBuilder.Entity(entity => { entity.HasOne(r => r.Product) .WithMany(p => p.Reviews) .HasForeignKey(r => r.ProductId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(r => r.Customer) .WithMany() .HasForeignKey(r => r.CustomerId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(r => r.Order) .WithMany() .HasForeignKey(r => r.OrderId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(r => r.ApprovedByUser) .WithMany() .HasForeignKey(r => r.ApprovedByUserId) .OnDelete(DeleteBehavior.SetNull); // Indexes for performance entity.HasIndex(e => e.ProductId); entity.HasIndex(e => e.CustomerId); entity.HasIndex(e => e.OrderId); entity.HasIndex(e => e.Rating); entity.HasIndex(e => e.IsApproved); entity.HasIndex(e => e.IsActive); entity.HasIndex(e => e.CreatedAt); // Composite indexes for common queries entity.HasIndex(e => new { e.ProductId, e.IsApproved, e.IsActive }); entity.HasIndex(e => new { e.CustomerId, e.ProductId }).IsUnique(); // One review per customer per product }); } }