using Microsoft.EntityFrameworkCore;
using SocialService.Domain.AggregatesModel.UserBlockAggregate;
using SocialService.Domain.SeedWork;

namespace SocialService.Infrastructure.Repositories;

/// <summary>
/// EN: Repository implementation for UserBlock aggregate.
/// VI: Implementation repository cho UserBlock aggregate.
/// </summary>
public class UserBlockRepository : IUserBlockRepository
{
    private readonly SocialServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

    public UserBlockRepository(SocialServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public UserBlock Add(UserBlock userBlock)
    {
        return _context.UserBlocks.Add(userBlock).Entity;
    }

    public void Remove(UserBlock userBlock)
    {
        _context.UserBlocks.Remove(userBlock);
    }

    public async Task<UserBlock?> GetByIdAsync(Guid id)
    {
        return await _context.UserBlocks.FindAsync(id);
    }

    public async Task<UserBlock?> GetByUsersAsync(Guid blockerId, Guid blockedId)
    {
        return await _context.UserBlocks
            .FirstOrDefaultAsync(ub => 
                EF.Property<Guid>(ub, "_blockerId") == blockerId &&
                EF.Property<Guid>(ub, "_blockedId") == blockedId);
    }

    public async Task<IEnumerable<UserBlock>> GetBlockedByUserAsync(Guid blockerId, int skip = 0, int take = 20)
    {
        return await _context.UserBlocks
            .Where(ub => EF.Property<Guid>(ub, "_blockerId") == blockerId)
            .OrderByDescending(ub => EF.Property<DateTime>(ub, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<IEnumerable<Guid>> GetBlockedUserIdsAsync(Guid blockerId)
    {
        return await _context.UserBlocks
            .Where(ub => EF.Property<Guid>(ub, "_blockerId") == blockerId)
            .Select(ub => EF.Property<Guid>(ub, "_blockedId"))
            .ToListAsync();
    }

    public async Task<bool> IsBlockedAsync(Guid blockerId, Guid blockedId)
    {
        return await _context.UserBlocks
            .AnyAsync(ub => 
                EF.Property<Guid>(ub, "_blockerId") == blockerId &&
                EF.Property<Guid>(ub, "_blockedId") == blockedId);
    }

    public async Task<bool> HasBlockBetweenAsync(Guid userId1, Guid userId2)
    {
        return await _context.UserBlocks
            .AnyAsync(ub => 
                (EF.Property<Guid>(ub, "_blockerId") == userId1 &&
                 EF.Property<Guid>(ub, "_blockedId") == userId2) ||
                (EF.Property<Guid>(ub, "_blockerId") == userId2 &&
                 EF.Property<Guid>(ub, "_blockedId") == userId1));
    }

    public async Task<int> GetBlockedCountAsync(Guid blockerId)
    {
        return await _context.UserBlocks
            .CountAsync(ub => EF.Property<Guid>(ub, "_blockerId") == blockerId);
    }

    #region Admin Methods

    public async Task<IEnumerable<UserBlock>> GetAllAsync(int skip, int take)
    {
        return await _context.UserBlocks
            .OrderByDescending(ub => EF.Property<DateTime>(ub, "_createdAt"))
            .Skip(skip)
            .Take(take)
            .ToListAsync();
    }

    public async Task<int> GetTotalCountAsync()
    {
        return await _context.UserBlocks.CountAsync();
    }

    #endregion
}

