/*
 * File: ShadowedRealms.Data/Contexts/GameDbContext.cs
 * Created: 2025-10-19
 * Last Modified: 2025-10-22
 * Description: Main Entity Framework database context for Shadowed Realms. Handles all game entities with kingdom-based data partitioning and server-authoritative design.
 * Last Edit Notes: Fixed PurchaseLog property references to match actual model properties
 */

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using ShadowedRealms.Core.Models.Alliance;
using ShadowedRealms.Core.Models.Kingdom;
using ShadowedRealms.Core.Models.Player;
using ShadowedRealms.Core.Models.Combat;
using ShadowedRealms.Core.Models.Purchase;
using System.Linq.Expressions;

namespace ShadowedRealms.Data.Contexts
{
    public class GameDbContext : IdentityDbContext<ApplicationUser, IdentityRole<int>, int>
    {
        private readonly ILogger<GameDbContext> _logger;
        private int? _currentKingdomId;

        public GameDbContext(DbContextOptions<GameDbContext> options, ILogger<GameDbContext> logger)
            : base(options)
        {
            _logger = logger;
        }

        // Core Game Entities
        public DbSet<Player> Players { get; set; }
        public DbSet<Alliance> Alliances { get; set; }
        public DbSet<Kingdom> Kingdoms { get; set; }
        public DbSet<CombatLog> CombatLogs { get; set; }
        public DbSet<PurchaseLog> PurchaseLogs { get; set; }

        // Kingdom Context Management
        public void SetKingdomContext(int kingdomId)
        {
            _currentKingdomId = kingdomId;
            _logger.LogInformation("Kingdom context set to {KingdomId}", kingdomId);
        }

        public int? GetCurrentKingdomId()
        {
            return _currentKingdomId;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            try
            {
                // Configure Identity tables with custom names
                ConfigureIdentityTables(modelBuilder);

                // Configure core game entities
                ConfigurePlayerEntity(modelBuilder);
                ConfigureAllianceEntity(modelBuilder);
                ConfigureKingdomEntity(modelBuilder);
                ConfigureCombatLogEntity(modelBuilder);
                ConfigurePurchaseLogEntity(modelBuilder);

                // Apply kingdom-scoped global query filters
                ApplyKingdomScopedFilters(modelBuilder);

                // Configure indexes for performance
                ConfigureIndexes(modelBuilder);

                _logger.LogInformation("Database model configuration completed successfully");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to configure database model");
                throw;
            }
        }

        private void ConfigureIdentityTables(ModelBuilder modelBuilder)
        {
            // Rename Identity tables to match our naming convention
            modelBuilder.Entity<ApplicationUser>().ToTable("Players_Auth");
            modelBuilder.Entity<IdentityRole<int>>().ToTable("Roles");
            modelBuilder.Entity<IdentityUserRole<int>>().ToTable("Player_Roles");
            modelBuilder.Entity<IdentityUserClaim<int>>().ToTable("Player_Claims");
            modelBuilder.Entity<IdentityUserLogin<int>>().ToTable("Player_Logins");
            modelBuilder.Entity<IdentityRoleClaim<int>>().ToTable("Role_Claims");
            modelBuilder.Entity<IdentityUserToken<int>>().ToTable("Player_Tokens");
        }

        private void ConfigurePlayerEntity(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Player>(entity =>
            {
                entity.HasKey(p => p.Id);
                entity.Property(p => p.Name).IsRequired().HasMaxLength(50);
                entity.Property(p => p.CastleLevel).IsRequired().HasDefaultValue(1);
                entity.Property(p => p.Power).IsRequired().HasDefaultValue(0);
                entity.Property(p => p.KingdomId).IsRequired();
                entity.Property(p => p.CreatedAt).IsRequired().HasDefaultValueSql("CURRENT_TIMESTAMP");
                entity.Property(p => p.LastActiveAt).IsRequired().HasDefaultValueSql("CURRENT_TIMESTAMP");
                entity.Property(p => p.IsActive).IsRequired().HasDefaultValue(true);

                // Relationships
                entity.HasOne(p => p.Kingdom)
                      .WithMany(k => k.Players)
                      .HasForeignKey(p => p.KingdomId)
                      .OnDelete(DeleteBehavior.Restrict);

                entity.HasOne(p => p.Alliance)
                      .WithMany(a => a.Members)
                      .HasForeignKey(p => p.AllianceId)
                      .OnDelete(DeleteBehavior.SetNull);

                // Indexes
                entity.HasIndex(p => p.KingdomId);
                entity.HasIndex(p => p.AllianceId);
                entity.HasIndex(p => p.Name);
                entity.HasIndex(p => p.Power);
            });
        }

        private void ConfigureAllianceEntity(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Alliance>(entity =>
            {
                entity.HasKey(a => a.Id);
                entity.Property(a => a.Name).IsRequired().HasMaxLength(50);
                entity.Property(a => a.Tag).IsRequired().HasMaxLength(5);
                entity.Property(a => a.Level).IsRequired().HasDefaultValue(1);
                entity.Property(a => a.Power).IsRequired().HasDefaultValue(0);
                entity.Property(a => a.KingdomId).IsRequired();
                entity.Property(a => a.CreatedAt).IsRequired().HasDefaultValueSql("CURRENT_TIMESTAMP");
                entity.Property(a => a.IsActive).IsRequired().HasDefaultValue(true);
                entity.Property(a => a.MaxMembers).IsRequired().HasDefaultValue(50);

                // Relationships
                entity.HasOne(a => a.Kingdom)
                      .WithMany(k => k.Alliances)
                      .HasForeignKey(a => a.KingdomId)
                      .OnDelete(DeleteBehavior.Restrict);

                entity.HasOne(a => a.Leader)
                      .WithOne()
                      .HasForeignKey<Alliance>(a => a.LeaderId)
                      .OnDelete(DeleteBehavior.Restrict);

                // Indexes
                entity.HasIndex(a => a.KingdomId);
                entity.HasIndex(a => a.Name);
                entity.HasIndex(a => a.Tag);
                entity.HasIndex(a => a.Power);
            });
        }

        private void ConfigureKingdomEntity(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Kingdom>(entity =>
            {
                entity.HasKey(k => k.Id);
                entity.Property(k => k.Name).IsRequired().HasMaxLength(50);
                entity.Property(k => k.Number).IsRequired();
                entity.Property(k => k.CreatedAt).IsRequired().HasDefaultValueSql("CURRENT_TIMESTAMP");
                entity.Property(k => k.IsActive).IsRequired().HasDefaultValue(true);
                entity.Property(k => k.MaxPopulation).IsRequired().HasDefaultValue(1500);
                entity.Property(k => k.CurrentPopulation).IsRequired().HasDefaultValue(0);

                // Indexes
                entity.HasIndex(k => k.Number).IsUnique();
                entity.HasIndex(k => k.Name);
            });
        }

        private void ConfigureCombatLogEntity(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<CombatLog>(entity =>
            {
                entity.HasKey(c => c.Id);
                entity.Property(c => c.AttackerPlayerId).IsRequired();
                entity.Property(c => c.DefenderPlayerId).IsRequired();
                entity.Property(c => c.KingdomId).IsRequired();
                entity.Property(c => c.CombatType).IsRequired();
                entity.Property(c => c.Result).IsRequired();
                entity.Property(c => c.Timestamp).IsRequired().HasDefaultValueSql("CURRENT_TIMESTAMP");

                // Relationships
                entity.HasOne<Player>()
                      .WithMany()
                      .HasForeignKey(c => c.AttackerPlayerId)
                      .OnDelete(DeleteBehavior.Restrict);

                entity.HasOne<Player>()
                      .WithMany()
                      .HasForeignKey(c => c.DefenderPlayerId)
                      .OnDelete(DeleteBehavior.Restrict);

                entity.HasOne<Kingdom>()
                      .WithMany()
                      .HasForeignKey(c => c.KingdomId)
                      .OnDelete(DeleteBehavior.Restrict);

                // Indexes
                entity.HasIndex(c => c.KingdomId);
                entity.HasIndex(c => c.AttackerPlayerId);
                entity.HasIndex(c => c.DefenderPlayerId);
                entity.HasIndex(c => c.Timestamp);
            });
        }

        private void ConfigurePurchaseLogEntity(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<PurchaseLog>(entity =>
            {
                entity.HasKey(p => p.Id);
                entity.Property(p => p.PlayerId).IsRequired();
                entity.Property(p => p.KingdomId).IsRequired();
                entity.Property(p => p.ProductId).IsRequired().HasMaxLength(100);
                entity.Property(p => p.Amount).IsRequired().HasColumnType("decimal(18,2)");
                entity.Property(p => p.Currency).IsRequired().HasMaxLength(10);
                // FIXED: Use PurchaseDate instead of Timestamp
                entity.Property(p => p.PurchaseDate).IsRequired().HasDefaultValueSql("CURRENT_TIMESTAMP");

                // Relationships
                entity.HasOne<Player>()
                      .WithMany()
                      .HasForeignKey(p => p.PlayerId)
                      .OnDelete(DeleteBehavior.Restrict);

                entity.HasOne<Kingdom>()
                      .WithMany()
                      .HasForeignKey(p => p.KingdomId)
                      .OnDelete(DeleteBehavior.Restrict);

                // Indexes
                entity.HasIndex(p => p.KingdomId);
                entity.HasIndex(p => p.PlayerId);
                // FIXED: Use PurchaseDate instead of Timestamp
                entity.HasIndex(p => p.PurchaseDate);
            });
        }

        private void ApplyKingdomScopedFilters(ModelBuilder modelBuilder)
        {
            // Apply global query filters to ensure kingdom-scoped data access
            // Players filter
            modelBuilder.Entity<Player>()
                .HasQueryFilter(p => _currentKingdomId == null || p.KingdomId == _currentKingdomId);

            // Alliances filter
            modelBuilder.Entity<Alliance>()
                .HasQueryFilter(a => _currentKingdomId == null || a.KingdomId == _currentKingdomId);

            // Combat logs filter
            modelBuilder.Entity<CombatLog>()
                .HasQueryFilter(c => _currentKingdomId == null || c.KingdomId == _currentKingdomId);

            // Purchase logs filter
            modelBuilder.Entity<PurchaseLog>()
                .HasQueryFilter(p => _currentKingdomId == null || p.KingdomId == _currentKingdomId);
        }

        private void ConfigureIndexes(ModelBuilder modelBuilder)
        {
            // Composite indexes for common query patterns
            modelBuilder.Entity<Player>()
                .HasIndex(p => new { p.KingdomId, p.Power })
                .HasDatabaseName("IX_Players_Kingdom_Power");

            modelBuilder.Entity<Player>()
                .HasIndex(p => new { p.KingdomId, p.AllianceId })
                .HasDatabaseName("IX_Players_Kingdom_Alliance");

            modelBuilder.Entity<Alliance>()
                .HasIndex(a => new { a.KingdomId, a.Power })
                .HasDatabaseName("IX_Alliances_Kingdom_Power");

            modelBuilder.Entity<CombatLog>()
                .HasIndex(c => new { c.KingdomId, c.Timestamp })
                .HasDatabaseName("IX_CombatLogs_Kingdom_Time");

            modelBuilder.Entity<PurchaseLog>()
                // FIXED: Use PurchaseDate instead of Timestamp
                .HasIndex(p => new { p.KingdomId, p.PurchaseDate })
                .HasDatabaseName("IX_PurchaseLogs_Kingdom_Time");
        }

        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                var result = await base.SaveChangesAsync(cancellationToken);

                if (result > 0)
                {
                    _logger.LogInformation("Successfully saved {ChangeCount} changes to database", result);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to save changes to database");
                throw;
            }
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                _logger.LogWarning("DbContext is not configured - this should only happen during design-time operations");
            }
        }
    }

    // Custom ApplicationUser for Identity integration
    public class ApplicationUser : IdentityUser<int>
    {
        public int? PlayerId { get; set; }
        public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
        public DateTime LastLoginAt { get; set; } = DateTime.UtcNow;
    }
}