namespace WalletService.Infrastructure.Repositories;

using Microsoft.EntityFrameworkCore;
using WalletService.Domain.AggregatesModel.PointAccountAggregate;
using WalletService.Domain.SeedWork;

/// <summary>
/// EN: Repository implementation for PointAccount aggregate
/// VI: Repository implementation cho aggregate PointAccount
/// </summary>
public class PointAccountRepository : IPointAccountRepository
{
    private readonly WalletServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

    public PointAccountRepository(WalletServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public async Task<PointAccount?> GetByIdAsync(Guid accountId)
    {
        return await _context.PointAccounts
            .FirstOrDefaultAsync(p => p.Id == accountId);
    }

    public async Task<PointAccount?> GetByUserIdAsync(Guid userId)
    {
        return await _context.PointAccounts
            .FirstOrDefaultAsync(p => p.UserId == userId);
    }

    public async Task<PointAccount?> GetByIdWithTransactionsAsync(Guid accountId)
    {
        return await _context.PointAccounts
            .Include(p => p.Transactions)
            .FirstOrDefaultAsync(p => p.Id == accountId);
    }

    public async Task<IEnumerable<PointTransaction>> GetTransactionsAsync(
        Guid accountId,
        int page = 1,
        int pageSize = 20)
    {
        return await _context.PointTransactions
            .Where(t => t.AccountId == accountId)
            .OrderByDescending(t => t.CreatedAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();
    }

    public async Task<int> CountTransactionsAsync(Guid accountId)
    {
        return await _context.PointTransactions
            .CountAsync(t => t.AccountId == accountId);
    }

    public async Task<bool> ExistsByUserIdAsync(Guid userId)
    {
        return await _context.PointAccounts
            .AnyAsync(p => p.UserId == userId);
    }

    public async Task<IEnumerable<PointAccount>> GetAccountsWithExpiringPointsAsync(DateTime expiryDate)
    {
        var accountIds = await _context.PointTransactions
            .Where(t => t.ExpiresAt != null && t.ExpiresAt <= expiryDate)
            .Select(t => t.AccountId)
            .Distinct()
            .ToListAsync();

        return await _context.PointAccounts
            .Where(p => accountIds.Contains(p.Id))
            .ToListAsync();
    }

    public PointAccount Add(PointAccount account)
    {
        return _context.PointAccounts.Add(account).Entity;
    }

    public void Update(PointAccount account)
    {
        _context.Entry(account).State = EntityState.Modified;
    }
}
