// EN: MerchantStaff repository implementation.
// VI: Implementation repository cho MerchantStaff.

using Microsoft.EntityFrameworkCore;
using MerchantService.Domain.AggregatesModel.MerchantStaffAggregate;
using MerchantService.Domain.SeedWork;

namespace MerchantService.Infrastructure.Repositories;

/// <summary>
/// EN: Repository implementation for MerchantStaff aggregate.
/// VI: Implementation repository cho MerchantStaff aggregate.
/// </summary>
public class MerchantStaffRepository : IMerchantStaffRepository
{
    private readonly MerchantServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

    public MerchantStaffRepository(MerchantServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    /// <inheritdoc />
    public async Task<MerchantStaff?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .Include(s => s.ShopAssignments)
            .Include(s => s.DeviceTokens)
            .FirstOrDefaultAsync(s => s.Id == id, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<MerchantStaff?> GetByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .Include(s => s.ShopAssignments)
            .FirstOrDefaultAsync(s => s.UserId == userId && s.StatusId != StaffStatus.Terminated.Id, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<MerchantStaff?> GetByUserIdAndMerchantIdAsync(Guid userId, Guid merchantId, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .Include(s => s.ShopAssignments)
            .FirstOrDefaultAsync(s => s.UserId == userId && s.MerchantId == merchantId && s.StatusId != StaffStatus.Terminated.Id, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantStaff>> GetByMerchantIdAsync(Guid merchantId, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .Include(s => s.ShopAssignments)
            .Where(s => s.MerchantId == merchantId && s.StatusId != StaffStatus.Terminated.Id)
            .OrderByDescending(s => s.JoinedAt)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantStaff>> GetByShopIdAsync(Guid shopId, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .Where(s => s.ShopAssignments.Any(a => a.ShopId == shopId) && s.StatusId != StaffStatus.Terminated.Id)
            .Include(s => s.ShopAssignments.Where(a => a.ShopId == shopId))
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<bool> ExistsByUserIdAndMerchantIdAsync(Guid userId, Guid merchantId, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .AnyAsync(s => s.UserId == userId && s.MerchantId == merchantId && s.StatusId != StaffStatus.Terminated.Id, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<MerchantStaff?> GetByEmailAsync(string email, CancellationToken cancellationToken = default)
    {
        return await _context.MerchantStaff
            .Include(s => s.ShopAssignments)
            .FirstOrDefaultAsync(s => s.Email == email && s.StatusId != StaffStatus.Terminated.Id, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<MerchantStaff>> GetByIdsAsync(IEnumerable<Guid> ids, CancellationToken cancellationToken = default)
    {
        var idList = ids.ToList();
        return await _context.MerchantStaff
            .Where(s => idList.Contains(s.Id))
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public MerchantStaff Add(MerchantStaff staff)
    {
        return _context.MerchantStaff.Add(staff).Entity;
    }

    /// <inheritdoc />
    public void Update(MerchantStaff staff)
    {
        _context.Entry(staff).State = EntityState.Modified;
    }
}
