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

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

namespace MerchantService.Infrastructure.Repositories;

/// <summary>
/// EN: Repository implementation for Shop aggregate.
/// VI: Implementation repository cho Shop aggregate.
/// </summary>
public class ShopRepository : IShopRepository
{
    private readonly MerchantServiceContext _context;

    public IUnitOfWork UnitOfWork => _context;

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

    /// <inheritdoc />
    public async Task<Shop?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .FirstOrDefaultAsync(s => s.Id == id && !EF.Property<bool>(s, "_isDeleted"), cancellationToken);
    }

    /// <inheritdoc />
    public async Task<Shop?> GetByIdWithBranchesAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .Include(s => s.Branches)
            .FirstOrDefaultAsync(s => s.Id == id && !EF.Property<bool>(s, "_isDeleted"), cancellationToken);
    }

    /// <inheritdoc />
    public async Task<Shop?> GetBySlugAsync(string slug, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .FirstOrDefaultAsync(s => EF.Property<string>(s, "_slug") == slug.ToLowerInvariant() && !EF.Property<bool>(s, "_isDeleted"), cancellationToken);
    }

    /// <inheritdoc />
    public async Task<bool> SlugExistsAsync(string slug, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .AnyAsync(s => EF.Property<string>(s, "_slug") == slug.ToLowerInvariant(), cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<Shop>> GetByMerchantIdAsync(Guid merchantId, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .Where(s => EF.Property<Guid>(s, "_merchantId") == merchantId && !EF.Property<bool>(s, "_isDeleted"))
            .OrderByDescending(s => EF.Property<DateTime>(s, "_createdAt"))
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<Shop>> GetByCategoryAsync(BusinessCategory category, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .Where(s => s.CategoryId == category.Id && !EF.Property<bool>(s, "_isDeleted") && s.StatusId == ShopStatus.Active.Id)
            .OrderByDescending(s => EF.Property<DateTime>(s, "_createdAt"))
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public Shop Add(Shop shop)
    {
        return _context.Shops.Add(shop).Entity;
    }

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

    /// <inheritdoc />
    public async Task<IReadOnlyList<Shop>> GetActiveShopsWithBranchesAsync(CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .Include(s => s.Branches)
            .Where(s => !EF.Property<bool>(s, "_isDeleted") && s.StatusId == ShopStatus.Active.Id)
            .ToListAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<(IReadOnlyList<Shop> Items, int TotalCount)> GetByMerchantIdPagedAsync(
        Guid merchantId, int? statusId, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
    {
        var query = _context.Shops
            .Where(s => EF.Property<Guid>(s, "_merchantId") == merchantId && !EF.Property<bool>(s, "_isDeleted"));

        if (statusId.HasValue)
        {
            query = query.Where(s => s.StatusId == statusId.Value);
        }

        var totalCount = await query.CountAsync(cancellationToken);

        var items = await query
            .OrderByDescending(s => EF.Property<bool>(s, "_isDefault"))
            .ThenByDescending(s => EF.Property<DateTime>(s, "_createdAt"))
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken);

        return (items, totalCount);
    }

    /// <inheritdoc />
    public async Task<Shop?> GetDefaultByMerchantIdAsync(Guid merchantId, CancellationToken cancellationToken = default)
    {
        return await _context.Shops
            .FirstOrDefaultAsync(s =>
                EF.Property<Guid>(s, "_merchantId") == merchantId
                && EF.Property<bool>(s, "_isDefault")
                && !EF.Property<bool>(s, "_isDeleted"),
                cancellationToken);
    }
}
