using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using OrderService.Domain.AggregatesModel.OrderAggregate;
using OrderService.Domain.SeedWork;
using OrderService.Infrastructure.EntityConfigurations;

namespace OrderService.Infrastructure;

/// <summary>
/// EN: Tenant provider interface consumed by OrderContext for global query filters.
///     Defined here to avoid Infrastructure depending on API layer.
/// VI: Interface tenant provider được OrderContext sử dụng cho global query filters.
///     Được định nghĩa ở đây để tránh Infrastructure phụ thuộc vào lớp API.
/// </summary>
public interface IOrderTenantProvider
{
    /// <summary>
    /// EN: Get current shop ID for tenant filtering.
    /// VI: Lấy shop ID hiện tại cho tenant filtering.
    /// </summary>
    Guid? GetCurrentShopId();

    /// <summary>
    /// EN: Check if tenant filter should be bypassed (admin/service calls).
    /// VI: Kiểm tra xem tenant filter có nên bị bỏ qua không (admin/service calls).
    /// </summary>
    bool ShouldBypassTenantFilter();
}

/// <summary>
/// EN: EF Core DbContext for OrderService with multi-tenant global query filters.
/// VI: EF Core DbContext cho OrderService với global query filters đa tenant.
/// </summary>
public class OrderContext : DbContext, IUnitOfWork
{
    private readonly IMediator _mediator;
    private readonly IOrderTenantProvider? _tenantProvider;
    private IDbContextTransaction? _currentTransaction;

    /// <summary>
    /// EN: Orders table.
    /// VI: Bảng Orders.
    /// </summary>
    public DbSet<Order> Orders => Set<Order>();

    /// <summary>
    /// EN: Read-only access to current transaction.
    /// VI: Truy cập chỉ đọc đến transaction hiện tại.
    /// </summary>
    public IDbContextTransaction? CurrentTransaction => _currentTransaction;

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

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

        System.Diagnostics.Debug.WriteLine("OrderContext::ctor - " + GetHashCode());
    }

    /// <summary>
    /// EN: Constructor with tenant provider for multi-tenant filtering.
    /// VI: Constructor với tenant provider cho filtering đa tenant.
    /// </summary>
    public OrderContext(
        DbContextOptions<OrderContext> options,
        IMediator mediator,
        IOrderTenantProvider tenantProvider) : base(options)
    {
        _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
        _tenantProvider = tenantProvider;

        System.Diagnostics.Debug.WriteLine("OrderContext::ctor (tenant) - " + GetHashCode());
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // EN: Apply entity configurations
        // VI: Áp dụng các cấu hình entity
        modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration());
        modelBuilder.ApplyConfiguration(new OrderStatusEntityTypeConfiguration());

        // EN: Apply global query filter for tenant isolation on Orders.
        //     Orders are filtered by shop_id from the current tenant context.
        //     Use .IgnoreQueryFilters() in queries that need cross-tenant access.
        // VI: Áp dụng global query filter cho cách ly tenant trên Orders.
        //     Orders được lọc theo shop_id từ tenant context hiện tại.
        //     Sử dụng .IgnoreQueryFilters() trong các queries cần truy cập cross-tenant.
        modelBuilder.Entity<Order>().HasQueryFilter(o =>
            _tenantProvider == null
            || _tenantProvider.ShouldBypassTenantFilter()
            || _tenantProvider.GetCurrentShopId() == null
            || EF.Property<Guid>(o, "_shopId") == _tenantProvider.GetCurrentShopId());
    }

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

        // EN: Save changes to database
        // VI: Lưu thay đổi vào database
        await base.SaveChangesAsync(cancellationToken);

        return true;
    }

    /// <summary>
    /// EN: Begin a new transaction if none is active.
    /// VI: Bắt đầu một transaction mới nếu không có transaction nào đang hoạt động.
    /// </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)
    {
        ArgumentNullException.ThrowIfNull(transaction);

        if (transaction != _currentTransaction)
            throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");

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

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

    /// <summary>
    /// EN: Dispatch all domain events from tracked entities.
    /// VI: Dispatch tất cả domain events từ các entities đang được track.
    /// </summary>
    private async Task DispatchDomainEventsAsync()
    {
        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);
        }
    }
}
