<?php

namespace App\Filters;

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

class ProductCategory extends BaseRule
{
    public const NAME = 'RULE_PRODUCT_CATEGORY';

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

    public function apply(Builder $builder, $operand): void
    {
        /** @var Criterion $criterion */
        foreach ($this->criteria as $criterion) {
            if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_CATEGORY) {
                $operator = $criterion->params['operator'];
                $categoryId = $criterion->params['value'];

                if (! $categoryId) {
                    return;
                }

                $uniqHash = md5(serialize($criterion));
                $joinAlias = 'product_category_' . $uniqHash;
                if ($builder->hasJoinAlias($joinAlias)) {
                    return;
                }

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

                if ($operator === 'contains') {
                    $builder->whereNotNull("$joinAlias.product_id", $operand);
                } elseif ($operator === 'not_contains') {
                    $builder->whereNull("$joinAlias.product_id", $operand);
                } else {
                    throw new InvalidArgumentException('Invalid operator: ' . $operator);
                }
            }
        }
    }
}
