using LctMonolith.Models.Database; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using EventLog = LctMonolith.Models.Database.EventLog; namespace LctMonolith.Database.Data; /// /// Main EF Core database context for gamification module (PostgreSQL provider expected). /// public class AppDbContext : IdentityDbContext, Guid> { public AppDbContext(DbContextOptions options) : base(options) { Database.EnsureCreated(); } // Rank related entities public DbSet Ranks => Set(); public DbSet RankMissionRules => Set(); public DbSet RankSkillRules => Set(); // Mission related entities public DbSet MissionCategories => Set(); public DbSet Missions => Set(); public DbSet PlayerMissions => Set(); public DbSet MissionSkillRewards => Set(); public DbSet MissionItemRewards => Set(); public DbSet MissionRankRules => Set(); // Skill related entities public DbSet Skills => Set(); public DbSet PlayerSkills => Set(); // Dialogue related entities public DbSet Dialogues => Set(); public DbSet DialogueMessages => Set(); public DbSet DialogueMessageResponseOptions => Set(); // Store and inventory public DbSet StoreItems => Set(); public DbSet UserInventoryItems => Set(); public DbSet Transactions => Set(); // System entities public DbSet EventLogs => Set(); public DbSet RefreshTokens => Set(); public DbSet Notifications => Set(); // Core profile / player chain public DbSet Players => Set(); public DbSet Profiles => Set(); protected override void OnModelCreating(ModelBuilder b) { base.OnModelCreating(b); // Player configuration b.Entity() .HasIndex(p => p.UserId) .IsUnique(); b.Entity() .HasOne() .WithOne() .HasForeignKey(p => p.UserId) .IsRequired(); // Rank configurations b.Entity() .HasIndex(r => r.ExpNeeded) .IsUnique(); b.Entity() .HasIndex(r => r.Title) .IsUnique(); // Skill configurations b.Entity() .HasIndex(s => s.Title) .IsUnique(); // MissionCategory configurations b.Entity() .HasIndex(mc => mc.Title) .IsUnique(); // Mission configurations b.Entity() .HasOne(m => m.MissionCategory) .WithMany(mc => mc.Missions) .HasForeignKey(m => m.MissionCategoryId) .IsRequired(); b.Entity() .HasOne(m => m.ParentMission) .WithMany(m => m.ChildMissions) .HasForeignKey(m => m.ParentMissionId) .IsRequired(false); // Dialogue relationship for Mission (optional dialogue for a mission) b.Entity() .HasOne(m => m.Dialogue) .WithOne(d => d.Mission) .HasForeignKey(m => m.DialogueId) .IsRequired(false); // MissionRankRule configurations b.Entity() .HasOne(mrr => mrr.Mission) .WithMany(m => m.MissionRankRules) .HasForeignKey(mrr => mrr.MissionId); b.Entity() .HasOne(mrr => mrr.Rank) .WithMany(r => r.MissionRankRules) .HasForeignKey(mrr => mrr.RankId); // MissionSkillReward configurations b.Entity() .HasKey(x => new { x.MissionId, x.SkillId }); b.Entity() .HasOne(msr => msr.Mission) .WithMany(m => m.MissionSkillRewards) .HasForeignKey(msr => msr.MissionId); b.Entity() .HasOne(msr => msr.Skill) .WithMany(s => s.MissionSkillRewards) .HasForeignKey(msr => msr.SkillId); // MissionItemReward configurations b.Entity() .HasOne(mir => mir.Mission) .WithMany(m => m.MissionItemRewards) .HasForeignKey(mir => mir.MissionId); // RankMissionRule composite key b.Entity().HasKey(x => new { x.RankId, x.MissionId }); b.Entity() .HasOne(rmr => rmr.Rank) .WithMany(r => r.RankMissionRules) .HasForeignKey(rmr => rmr.RankId); b.Entity() .HasOne(rmr => rmr.Mission) .WithMany(m => m.RankMissionRules) .HasForeignKey(rmr => rmr.MissionId); // RankSkillRule composite key b.Entity().HasKey(x => new { x.RankId, x.SkillId }); b.Entity() .HasOne(rsr => rsr.Rank) .WithMany(r => r.RankSkillRules) .HasForeignKey(rsr => rsr.RankId); b.Entity() .HasOne(rsr => rsr.Skill) .WithMany(s => s.RankSkillRules) .HasForeignKey(rsr => rsr.SkillId); // PlayerSkill composite key b.Entity().HasKey(x => new { x.PlayerId, x.SkillId }); b.Entity() .HasOne(ps => ps.Player) .WithMany(p => p.PlayerSkills) .HasForeignKey(ps => ps.PlayerId); b.Entity() .HasOne(ps => ps.Skill) .WithMany(s => s.PlayerSkills) .HasForeignKey(ps => ps.SkillId); // PlayerMission composite key b.Entity().HasKey(x => new { x.PlayerId, x.MissionId }); b.Entity() .HasOne(pm => pm.Player) .WithMany(p => p.PlayerMissions) .HasForeignKey(pm => pm.PlayerId); b.Entity() .HasOne(pm => pm.Mission) .WithMany(m => m.PlayerMissions) .HasForeignKey(pm => pm.MissionId); // Dialogue configurations (Mission required for Dialogue) b.Entity() .HasOne(d => d.Mission) .WithOne(m => m.Dialogue) .HasForeignKey(d => d.MissionId) .IsRequired(); // Dialogue -> DialogueMessage relationships (three distinct message slots) b.Entity() .HasOne(d => d.InitialDialogueMessage) .WithMany() .HasForeignKey(d => d.InitialDialogueMessageId) .OnDelete(DeleteBehavior.Restrict); b.Entity() .HasOne(d => d.InterimDialogueMessage) .WithMany() .HasForeignKey(d => d.InterimDialogueMessageId) .OnDelete(DeleteBehavior.Restrict); b.Entity() .HasOne(d => d.EndDialogueMessage) .WithMany() .HasForeignKey(d => d.EndDialogueMessageId) .OnDelete(DeleteBehavior.Restrict); // DialogueMessageResponseOption configurations b.Entity() .HasOne(dmro => dmro.ParentDialogueMessage) .WithMany(dm => dm.DialogueMessageResponseOptions) .HasForeignKey(dmro => dmro.ParentDialogueMessageId) .IsRequired(); b.Entity() .HasOne(dmro => dmro.DestinationDialogueMessage) .WithMany() .HasForeignKey(dmro => dmro.DestinationDialogueMessageId) .IsRequired(false) .OnDelete(DeleteBehavior.Restrict); // UserInventoryItem composite key & relationships b.Entity() .HasKey(ui => new { ui.UserId, ui.StoreItemId }); b.Entity() .HasOne(ui => ui.User) .WithMany(u => u.Inventory) .HasForeignKey(ui => ui.UserId); b.Entity() .HasOne(ui => ui.StoreItem) .WithMany(si => si.UserInventory) .HasForeignKey(ui => ui.StoreItemId); // Refresh token index unique b.Entity().HasIndex(x => x.Token).IsUnique(); // ---------- Performance indexes ---------- b.Entity().HasIndex(ps => ps.SkillId); b.Entity().HasIndex(e => new { e.UserId, e.Type, e.CreatedAt }); b.Entity().HasIndex(i => i.IsActive); b.Entity().HasIndex(t => new { t.UserId, t.CreatedAt }); b.Entity().HasIndex(n => new { n.UserId, n.IsRead, n.CreatedAt }); } }