using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using MembershipService.Domain.AggregatesModel.ExperienceAggregate;
using MembershipService.Domain.AggregatesModel.LevelAggregate;
using MembershipService.Domain.AggregatesModel.MemberAggregate;
using MembershipService.Domain.AggregatesModel.StampCardAggregate;
using MembershipService.Domain.SeedWork;

namespace MembershipService.Infrastructure;

/// <summary>
/// EN: Database context for Membership Service.
/// VI: Database context cho Membership Service.
/// </summary>
public class MembershipServiceContext : DbContext, IUnitOfWork
{
    private readonly IMediator _mediator;
    private IDbContextTransaction? _currentTransaction;

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

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

    /// <summary>
    /// EN: Members table.
    /// VI: Bảng members.
    /// </summary>
    public DbSet<Member> Members { get; set; } = null!;

    /// <summary>
    /// EN: Level definitions table.
    /// VI: Bảng level definitions.
    /// </summary>
    public DbSet<LevelDefinition> LevelDefinitions { get; set; } = null!;

    /// <summary>
    /// EN: Level benefits table.
    /// VI: Bảng level benefits.
    /// </summary>
    public DbSet<LevelBenefit> LevelBenefits { get; set; } = null!;

    /// <summary>
    /// EN: Experience transactions table.
    /// VI: Bảng experience transactions.
    /// </summary>
    public DbSet<ExperienceTransaction> ExperienceTransactions { get; set; } = null!;

    /// <summary>
    /// EN: Stamp cards table.
    /// VI: Bang stamp cards.
    /// </summary>
    public DbSet<StampCard> StampCards { 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: Ignore Enumeration types to prevent EF Core discovery issues
        // VI: Bỏ qua Enumeration types để tránh lỗi EF Core discovery
        modelBuilder.Ignore<ExperienceSource>();

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

    /// <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;

        var domainEntities = ChangeTracker
            .Entries<Entity>()
            .Where(x => x.Entity.DomainEvents.Any())
            .ToList();

        var domainEvents = domainEntities
            .SelectMany(x => x.Entity.DomainEvents)
            .ToList();

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

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