using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using MiningService.Domain.AggregatesModel.MinerAggregate;
using MiningService.Domain.AggregatesModel.CircleAggregate;
using MiningService.Domain.AggregatesModel.ReferralAggregate;
using MiningService.Domain.SeedWork;

namespace MiningService.Infrastructure;

/// <summary>
/// EN: Database context for Mining Service.
/// VI: Database context cho Mining Service.
/// </summary>
public class MiningServiceContext : DbContext, IUnitOfWork
{
    private IDbContextTransaction? _currentTransaction;

    public DbSet<Miner> Miners { get; set; } = null!;
    public DbSet<MiningHistory> MiningHistories { get; set; } = null!;
    public DbSet<Circle> Circles { get; set; } = null!;
    public DbSet<CircleMember> CircleMembers { get; set; } = null!;
    public DbSet<Referral> Referrals { get; set; } = null!;

    public bool HasActiveTransaction => _currentTransaction != null;

    public MiningServiceContext(DbContextOptions<MiningServiceContext> options) 
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(MiningServiceContext).Assembly);
    }

    public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
    {
        await SaveChangesAsync(cancellationToken);
        return true;
    }

    public async Task<IDbContextTransaction?> BeginTransactionAsync(CancellationToken cancellationToken = default)
    {
        if (_currentTransaction != null) return null;
        _currentTransaction = await Database.BeginTransactionAsync(cancellationToken);
        return _currentTransaction;
    }

    public async Task CommitTransactionAsync(IDbContextTransaction 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)
            {
                await _currentTransaction.DisposeAsync();
                _currentTransaction = null;
            }
        }
    }

    public void RollbackTransaction()
    {
        try
        {
            _currentTransaction?.Rollback();
        }
        finally
        {
            if (_currentTransaction != null)
            {
                _currentTransaction.Dispose();
                _currentTransaction = null;
            }
        }
    }
}

