namespace Netina.Repository.Handlers.Products;

public class GetProductsQueryHandler(
    IRepositoryWrapper repositoryWrapper,
    IMediator mediator,
    ICurrentUserService currentUserService)
    : IRequestHandler<GetProductsQuery, GetProductsResponseDto>
{
    public async Task<GetProductsResponseDto> Handle(GetProductsQuery request, CancellationToken cancellationToken)
    {
        var response = new GetProductsResponseDto();
        var products = repositoryWrapper.SetRepository<Product>().TableNoTracking;
        if (currentUserService.JwtToken == null)
            products = products.Where(p => p.BeDisplayed);
        var roleClaim = currentUserService.JwtToken?.Claims.FirstOrDefault(c => c.Type == "role");
        if (roleClaim  != null && roleClaim.Value.Contains("Customer"))
            products = products.Where(p => p.BeDisplayed);

        if (request.IsActive != null)
            products = products.Where(p => p.IsEnable == request.IsActive);
        if (request.SortBy != QuerySortBy.None)
        {
            products = request.SortBy switch
            {
                QuerySortBy.Cheapest => products.OrderBy(p => p.Cost),
                QuerySortBy.MostExpensive => products.OrderByDescending(p => p.Cost),
                QuerySortBy.MostPopular => products.OrderByDescending(p => p.Rate),
                QuerySortBy.MostViewed => products.OrderByDescending(p => p.Viewed),
                _ => products
            };
        }
        else
            products = products.OrderByDescending(p => p.CreatedAt);

        if (request.ProductName != null)
            products = products.OrderByDescending(p => EF.Functions.TrigramsSimilarity(p.PersianName, request.ProductName));

        if (request.CategoryId != default)
        {
            var cats = await mediator.Send(new GetProductCategoryChildrenQuery(request.CategoryId), cancellationToken);
            products = products.Where(p => cats.Contains(p.CategoryId));
        }
        if (request.BrandIds is { Length: > 0 })
            products = products.Where(p => request.BrandIds.Contains(p.BrandId));
        if (request.MinPrice > -1)
            products = products.Where(p => p.Cost >= request.MinPrice);
        if (request.MaxPrice > 0)
            products = products.Where(p => p.Cost <= request.MaxPrice);
        

        if (request.SpecialOffer != null)
        {
            if (request.SpecialOffer.Value)
            {
                var productDiscount = await repositoryWrapper.SetRepository<ProductDiscount>()
                .TableNoTracking
                    .Where(d => d.HasCode == false && d.IsSpecialOffer && d.ExpireDate.Date >= DateTime.Today.Date)
                    .OrderByDescending(d => d.CreatedAt)
                .Select(d => d.ProductId)
                    .ToListAsync(cancellationToken);

                products = products.Where(p => productDiscount.Contains(p.Id));
            }
        }

        response.Pager.CurrentPage = request.Page;
        response.Pager.TotalItems = await products.CountAsync(cancellationToken);
        response.Pager.TotalPage = response.Pager.TotalItems % 20 == 0 ? response.Pager.TotalItems / 20 : (response.Pager.TotalItems / 20) + 1;

        if (await products.AnyAsync(cancellationToken))
        {
            response.Filters.Price.MaximumValue = await products.MaxAsync(p => p.Cost, cancellationToken);
            response.Filters.Price.MinimumValue = await products.MinAsync(p => p.Cost, cancellationToken);
        }


        List<ProductSDto> productSDtos = await products
            .Skip(request.Page * 20)
            .Take(20)
            .Select(ProductMapper.ProjectToSDto)
            .ToListAsync(cancellationToken);


        foreach (var productSDto in productSDtos)
        {
            await mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);
        }

        response.Products = productSDtos;
        return response;
    }

}