<?php

namespace App\Helpers\Ecommerce\Product\Filter;

use App\Rules\CommaSeparatedIntegers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class ProductFilterer
{
    /*
     * Query builder
     */
    protected $queryBuilder;

    /**
     * Request
     */
    protected Request $request;

    /**
     * Product filterer
     */
    public function __construct($queryBuilder, $request)
    {
        $this->queryBuilder = $queryBuilder;
        $this->request = $request;
    }

    /**
     * Create "new" class via static method
     */
    public static function make($queryBuilder, $request): self
    {
        return new self($queryBuilder, $request);
    }

    /**
     * Validate the request
     */
    public function validateRequest(): \Illuminate\Contracts\Validation\Validator
    {
        return Validator::make($this->request->all(), [
            'ids' => ['nullable', 'string', new CommaSeparatedIntegers],
            'brands' => ['nullable', 'string', new CommaSeparatedIntegers],
            'categories' => ['nullable', 'string', new CommaSeparatedIntegers],
            'name' => ['nullable', 'string', 'max:255'],
            'min_price' => ['nullable', 'numeric'],
            'max_price' => ['nullable', 'numeric'],
            'backorder' => ['nullable', 'in:0,1'],
            'properties' => ['nullable', 'array'],
        ]);
    }

    /**
     * Sort
     */
    public function filter()
    {
        if ($this->validateRequest()->fails()) {
            return $this->queryBuilder;
        }

        if ($this->request->filled('properties')) {
            foreach ($this->request->input('properties') as $attributeSlug => $values) {
                $valuesArray = explode(',', $values);

                $this->queryBuilder->where(function ($query) use ($attributeSlug, $valuesArray) {
                    foreach ($valuesArray as $value) {
                        $query->orWhereJsonContains("properties_json->{$attributeSlug}", $value);
                    }
                });
            }
        }

        if ($this->request->filled('brands')) {
            $this->queryBuilder->whereIntegerInRaw('products.brand_id', explode(',', $this->request->brands));
        }

        if ($this->request->filled('categories')) {
            $this->queryBuilder->whereIn(
                column: 'id',
                values: (
                    fn ($query) => $query->from('category_product')
                        ->select('product_id')
                        ->distinct('product_id')
                        ->whereIn('category_id', explode(',', $this->request->categories))
                )
            );
        }

        if ($this->request->filled('name')) {
            $operator = $this->queryBuilder->getConnection()->getDriverName() === 'pgsql' ? 'ilike' : 'like';
            $this->queryBuilder->where('products.name', $operator, $this->request->name.'%');
        }

        if ($this->request->filled('min_price')) {
            if ($this->queryBuilder->getConnection()->getDriverName() === 'pgsql') {
                $this->queryBuilder->whereRaw('products.price_amount::NUMERIC >= ?', [$this->request->float('min_price')]);
            } else {
                $this->queryBuilder->where('products.price_amount', '>=', $this->request->float('min_price'));
            }
        }

        if ($this->request->filled('max_price')) {
            if ($this->queryBuilder->getConnection()->getDriverName() === 'pgsql') {
                $this->queryBuilder->whereRaw('products.price_amount::NUMERIC <= ?', [$this->request->float('max_price')]);
            } else {
                $this->queryBuilder->where('products.price_amount', '<=', $this->request->float('max_price'));
            }
        }

        if ($this->request->filled('backorder')) {
            $this->queryBuilder->where('products.backorder', $this->request->backorder);
        }

        return $this->queryBuilder;
    }
}
