using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using IamService.Domain.AggregatesModel.UserAggregate;
using IamService.Domain.AggregatesModel.RoleAggregate;
using IamService.Domain.AggregatesModel.OrganizationAggregate;
using IamService.Domain.AggregatesModel.GroupAggregate;
using IamService.Domain.AggregatesModel.VerificationAggregate;
using IamService.Domain.AggregatesModel.AccessRequestAggregate;
using IamService.Domain.AggregatesModel.AccessReviewAggregate;
using IamService.Domain.AggregatesModel.PrivilegedAccessAggregate;
using IamService.Domain.AggregatesModel.AuditAggregate;
using IamService.Domain.AggregatesModel.ComplianceAggregate;
using IamService.Domain.SeedWork;

namespace IamService.Infrastructure;


/// <summary>
/// EN: Database context for IAM Service with Identity support.
/// VI: Database context cho IAM Service với Identity support.
/// </summary>
public class IamServiceContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>, IUnitOfWork
{
    private readonly IMediator _mediator;
    private IDbContextTransaction? _currentTransaction;

    public IamServiceContext(DbContextOptions<IamServiceContext> options)
        : base(options)
    {
        _mediator = null!;
    }

    public IamServiceContext(DbContextOptions<IamServiceContext> options, IMediator mediator)
        : base(options)
    {
        _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
    }

    /// <summary>
    /// EN: User statuses table.
    /// VI: Bảng trạng thái user.
    /// </summary>
    public DbSet<UserStatus> UserStatuses { get; set; } = null!;

    /// <summary>
    /// EN: User profiles table.
    /// VI: Bảng profile user.
    /// </summary>
    public DbSet<UserProfile> UserProfiles { get; set; } = null!;

    /// <summary>
    /// EN: Organizations table.
    /// VI: Bảng tổ chức.
    /// </summary>
    public DbSet<Organization> Organizations { get; set; } = null!;

    /// <summary>
    /// EN: Organization statuses table.
    /// VI: Bảng trạng thái tổ chức.
    /// </summary>
    public DbSet<OrganizationStatus> OrganizationStatuses { get; set; } = null!;

    /// <summary>
    /// EN: Groups table.
    /// VI: Bảng nhóm.
    /// </summary>
    public DbSet<Group> Groups { get; set; } = null!;

    /// <summary>
    /// EN: Group members table.
    /// VI: Bảng thành viên nhóm.
    /// </summary>
    public DbSet<GroupMember> GroupMembers { get; set; } = null!;

    /// <summary>
    /// EN: Group permissions table.
    /// VI: Bảng quyền nhóm.
    /// </summary>
    public DbSet<GroupPermission> GroupPermissions { get; set; } = null!;

    /// <summary>
    /// EN: Group roles table.
    /// VI: Bảng vai trò nhóm.
    /// </summary>
    public DbSet<GroupRole> GroupRoles { get; set; } = null!;

    /// <summary>
    /// EN: Identity verifications table.
    /// VI: Bảng xác thực danh tính.
    /// </summary>
    public DbSet<IdentityVerification> IdentityVerifications { get; set; } = null!;

    /// <summary>
    /// EN: Verification types table.
    /// VI: Bảng loại xác thực.
    /// </summary>
    public DbSet<VerificationType> VerificationTypes { get; set; } = null!;

    /// <summary>
    /// EN: Verification statuses table.
    /// VI: Bảng trạng thái xác thực.
    /// </summary>
    public DbSet<VerificationStatus> VerificationStatuses { get; set; } = null!;

    /// <summary>
    /// EN: Profile attributes table.
    /// VI: Bảng profile attributes.
    /// </summary>
    public DbSet<ProfileAttribute> ProfileAttributes { get; set; } = null!;

    /// <summary>
    /// EN: Profile attribute types table.
    /// VI: Bảng loại profile attribute.
    /// </summary>
    public DbSet<ProfileAttributeType> ProfileAttributeTypes { get; set; } = null!;

    /// <summary>
    /// EN: Access requests table.
    /// VI: Bảng yêu cầu truy cập.
    /// </summary>
    public DbSet<AccessRequest> AccessRequests { get; set; } = null!;

    /// <summary>
    /// EN: Access request approvers table.
    /// VI: Bảng người phê duyệt yêu cầu truy cập.
    /// </summary>
    public DbSet<AccessRequestApprover> AccessRequestApprovers { get; set; } = null!;

    /// <summary>
    /// EN: Access reviews table.
    /// VI: Bảng đánh giá truy cập.
    /// </summary>
    public DbSet<AccessReview> AccessReviews { get; set; } = null!;

    /// <summary>
    /// EN: Access review items table.
    /// VI: Bảng items đánh giá truy cập.
    /// </summary>
    public DbSet<AccessReviewItem> AccessReviewItems { get; set; } = null!;

    /// <summary>
    /// EN: Privileged access grants table.
    /// VI: Bảng cấp quyền truy cập đặc quyền.
    /// </summary>
    public DbSet<PrivilegedAccessGrant> PrivilegedAccessGrants { get; set; } = null!;

    /// <summary>
    /// EN: Audit logs table.
    /// VI: Bảng audit logs.
    /// </summary>
    public DbSet<AuditLog> AuditLogs { get; set; } = null!;

    /// <summary>
    /// EN: Compliance reports table.
    /// VI: Bảng báo cáo tuân thủ.
    /// </summary>
    public DbSet<ComplianceReport> ComplianceReports { get; set; } = null!;

    /// <summary>
    /// EN: Compliance violations table.
    /// VI: Bảng vi phạm tuân thủ.
    /// </summary>
    public DbSet<ComplianceViolation> ComplianceViolations { get; set; } = null!;

    /// <summary>
    /// EN: Check if there's an active transaction.
    /// VI: Kiểm tra xem có transaction đang hoạt động không.
    /// </summary>
    public bool HasActiveTransaction => _currentTransaction != null;

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

        // EN: Apply entity configurations
        // VI: Áp dụng entity configurations
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(IamServiceContext).Assembly);

        // EN: Configure Identity tables with custom names
        // VI: Cấu hình bảng Identity với tên tùy chỉnh
        modelBuilder.Entity<ApplicationUser>(b =>
        {
            b.ToTable("users");
            b.Property<string>("_firstName").HasColumnName("first_name").HasMaxLength(100);
            b.Property<string>("_lastName").HasColumnName("last_name").HasMaxLength(100);
            b.Property<DateTime>("_createdAt").HasColumnName("created_at");
            b.Property<DateTime?>("_lastLoginAt").HasColumnName("last_login_at");
            b.Ignore(u => u.FirstName);
            b.Ignore(u => u.LastName);
            b.Ignore(u => u.FullName);
            b.Ignore(u => u.Status);
            b.Ignore(u => u.CreatedAt);
            b.Ignore(u => u.LastLoginAt);
            b.Ignore(u => u.DomainEvents);
        });
        modelBuilder.Entity<ApplicationRole>(b =>
        {
            b.ToTable("roles");
            b.Property<string>("_description").HasColumnName("description");
            b.Property<bool>("_isSystemRole").HasColumnName("is_system_role");
            b.Property<DateTime>("_createdAt").HasColumnName("created_at");
            b.Ignore(r => r.Description);
            b.Ignore(r => r.IsSystemRole);
            b.Ignore(r => r.CreatedAt);
            b.Ignore(r => r.DomainEvents);
        });
        modelBuilder.Entity<IdentityUserRole<Guid>>().ToTable("user_roles");
        modelBuilder.Entity<IdentityUserClaim<Guid>>().ToTable("user_claims");
        modelBuilder.Entity<IdentityUserLogin<Guid>>().ToTable("user_logins");
        modelBuilder.Entity<IdentityUserToken<Guid>>().ToTable("user_tokens");
        modelBuilder.Entity<IdentityRoleClaim<Guid>>().ToTable("role_claims");

        // EN: Seed UserStatus enumeration
        // VI: Seed UserStatus enumeration
        modelBuilder.Entity<UserStatus>().ToTable("user_statuses");
        modelBuilder.Entity<UserStatus>().HasData(
            UserStatus.Active,
            UserStatus.Locked,
            UserStatus.Disabled,
            UserStatus.PendingVerification
        );
    }

    /// <summary>
    /// EN: Save changes and dispatch domain events.
    /// VI: Lưu thay đổi và dispatch domain events.
    /// </summary>
    public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
    {
        // EN: Dispatch domain events before saving
        // VI: Dispatch domain events trước khi lưu
        await DispatchDomainEventsAsync(cancellationToken);

        await base.SaveChangesAsync(cancellationToken);

        return true;
    }

    /// <summary>
    /// EN: Begin a new transaction.
    /// VI: Bắt đầu transaction mới.
    /// </summary>
    public async Task<IDbContextTransaction?> BeginTransactionAsync()
    {
        if (_currentTransaction != null) return null;

        _currentTransaction = await Database.BeginTransactionAsync(System.Data.IsolationLevel.ReadCommitted);

        return _currentTransaction;
    }

    /// <summary>
    /// EN: Commit the current transaction.
    /// VI: Commit transaction hiện tại.
    /// </summary>
    public async Task CommitTransactionAsync(IDbContextTransaction transaction)
    {
        if (transaction == null) throw new ArgumentNullException(nameof(transaction));
        if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");

        try
        {
            await SaveChangesAsync();
            await transaction.CommitAsync();
        }
        catch
        {
            await RollbackTransactionAsync();
            throw;
        }
        finally
        {
            if (_currentTransaction != null)
            {
                _currentTransaction.Dispose();
                _currentTransaction = null;
            }
        }
    }

    /// <summary>
    /// EN: Rollback the current transaction.
    /// VI: Rollback transaction hiện tại.
    /// </summary>
    public async Task RollbackTransactionAsync()
    {
        try
        {
            if (_currentTransaction != null)
            {
                await _currentTransaction.RollbackAsync();
            }
        }
        finally
        {
            if (_currentTransaction != null)
            {
                _currentTransaction.Dispose();
                _currentTransaction = null;
            }
        }
    }

    private async Task DispatchDomainEventsAsync(CancellationToken cancellationToken)
    {
        if (_mediator == null) return;

        // EN: Get domain events from ApplicationUser (extends IdentityUser, not Entity)
        // VI: Lấy domain events từ ApplicationUser (extends IdentityUser, không phải Entity)
        var userEntities = ChangeTracker
            .Entries<ApplicationUser>()
            .Where(x => x.Entity.DomainEvents.Any())
            .ToList();

        var userEvents = userEntities
            .SelectMany(x => x.Entity.DomainEvents)
            .ToList();

        userEntities.ForEach(entity => entity.Entity.ClearDomainEvents());

        // EN: Get domain events from Entity-based aggregates (Organization, Group, IdentityVerification, etc.)
        // VI: Lấy domain events từ các aggregates kế thừa Entity (Organization, Group, IdentityVerification, v.v.)
        var entityEntries = ChangeTracker
            .Entries<Entity>()
            .Where(x => x.Entity.DomainEvents.Any())
            .ToList();

        var entityEvents = entityEntries
            .SelectMany(x => x.Entity.DomainEvents)
            .ToList();

        entityEntries.ForEach(entity => entity.Entity.ClearDomainEvents());

        // EN: Combine all domain events and dispatch
        // VI: Gộp tất cả domain events và dispatch
        var allDomainEvents = userEvents.Concat(entityEvents).ToList();

        foreach (var domainEvent in allDomainEvents)
        {
            await _mediator.Publish(domainEvent, cancellationToken);
        }
    }
}
