<?php

namespace App\Filters;

use Openguru\OpenCartFramework\CriteriaBuilder\Criterion;
use Openguru\OpenCartFramework\CriteriaBuilder\Rules\BaseRule;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;

class ProductCategories extends BaseRule
{
    public const NAME = 'RULE_PRODUCT_CATEGORIES';

    public static function initWithDefaults(): BaseRule
    {
        return new self(static::NAME, [
            'product_category_ids' => new Criterion(static::CRITERIA_OPTION_PRODUCT_CATEGORIES, [
                'operator' => static::CRITERIA_OPERATOR_CONTAINS,
                'value' => [],
            ])
        ]);
    }

    public function apply(Builder $builder, $operand): void
    {
        /** @var Criterion $criterion */
        foreach ($this->criteria as $criterion) {
            if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_CATEGORIES) {
                $uniqHash = md5(serialize($criterion));
                $joinAlias = 'product_category_' . $uniqHash;
                if ($builder->hasJoinAlias($joinAlias)) {
                    return;
                }

                $operator = $criterion->params['operator'];
                $categoryIds = $criterion->params['value'];

                $builder->join(
                    db_table('product_to_category') . " AS $joinAlias",
                    function (JoinClause $join) use ($joinAlias, $categoryIds) {
                        $join->on('products.product_id', '=', "$joinAlias.product_id");
                        if ($categoryIds) {
                            $join->whereIn("$joinAlias.category_id", $categoryIds);
                        }
                    },
                    'left'
                );

                if ($operator === 'contains' && ! $categoryIds) {
                    $builder->whereNull("$joinAlias.product_id", $operand);
                } elseif ($operator === 'not_contains' && ! $categoryIds) {
                    $builder->whereNotNull("$joinAlias.product_id", $operand);
                } elseif ($operator === 'contains') {
                    $builder->whereNotNull("$joinAlias.product_id", $operand);
                } else {
                    $builder->whereNull("$joinAlias.product_id", $operand);
                }
            }
        }
    }
}
