<?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 ProductAttribute extends BaseRule
{
    public const NAME = 'RULE_PRODUCT_ATTRIBUTE';

    public static function initWithDefaults(): BaseRule
    {
        return new self(static::NAME, [
            'product_attribute' => new Criterion(static::CRITERIA_OPTION_PRODUCT_ATTRIBUTE, [
                'attribute_id' => null,
                'operator' => static::CRITERIA_OPERATOR_CONTAINS,
                'keyword' => '',
                'language_id' => config('language_id'),
            ]),
        ]);
    }

    public function apply(Builder $builder, $operand): void
    {
        foreach ($this->criteria as $criterion) {
            if ($criterion->type === static::CRITERIA_OPTION_PRODUCT_ATTRIBUTE) {
                $facetHash = md5(serialize($criterion));
                $joinAlias = 'product_attributes_facet_' . $facetHash;
                if ($builder->hasJoinAlias($joinAlias)) {
                    return;
                }

                $operator = static::$stringCompareOperators[$criterion->params['operator']];

                $languageId = $criterion->params['language_id'] ?? null;
                if (! $languageId) {
                    throw new InvalidArgumentException('language_id is required for the product attribute filter');
                }

                $builder->leftJoin(
                    db_table('product_attribute') . " AS $joinAlias",
                    function (JoinClause $join) use ($criterion, $joinAlias, $operator, $languageId) {
                        $join->on('products.product_id', '=', "$joinAlias.product_id")
                            ->where("$joinAlias.attribute_id", '=', $criterion->params['attribute_id'])
                            ->where("$joinAlias.language_id", '=', $languageId);

                        if ($operator !== 'is_empty' && $operator !== 'is_not_empty') {
                            $this->criterionStringCondition(
                                $join,
                                $criterion,
                                "$joinAlias.text",
                                'and'
                            );
                        }
                    }
                );

                if ($operator === 'is_empty') {
                    $builder->whereNull("$joinAlias.product_id", $operand);
                } else {
                    $builder->whereNotNull("$joinAlias.product_id", $operand);
                }
            }
        }
    }
}
