<?php

declare(strict_types=1);

namespace App\Filters;

use InvalidArgumentException;
use Openguru\OpenCartFramework\CriteriaBuilder\Criterion;
use Openguru\OpenCartFramework\CriteriaBuilder\Exceptions\CriteriaBuilderException;
use Openguru\OpenCartFramework\CriteriaBuilder\Rules\BaseRule;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use RuntimeException;

class ProductQuantity extends BaseRule
{
    public const NAME = 'RULE_PRODUCT_QUANTITY';

    public static function initWithDefaults(): BaseRule
    {
        return new self(static::NAME, [
            'product_quantity' => new Criterion(static::CRITERIA_OPTION_NUMBER, [
                'operator' => static::CRITERIA_OPERATOR_GREATER_OR_EQUAL,
                'value' => [0, null],
            ]),
        ]);
    }

    public function apply(Builder $builder, $operand): void
    {
        /** @var Criterion|null $productQuantityCriterion */
        $productQuantityCriterion = $this->criteria['product_quantity'] ?? null;

        if (! $productQuantityCriterion) {
            throw new RuntimeException('Product Quantity rule criterion is not found.');
        }

        $column = 'products.quantity';

        if (! isset(static::$numberCompareOperators[$productQuantityCriterion->params['operator']])) {
            throw new InvalidArgumentException('Invalid operator: ' . $productQuantityCriterion->params['operator']);
        }

        $numberOperator = static::$numberCompareOperators[$productQuantityCriterion->params['operator']];

        $value = $this->prepareValue(
            $numberOperator,
            $productQuantityCriterion->params['value'],
        );

        if ($numberOperator === 'BETWEEN') {
            $builder->whereBetween($column, $value, $operand);
        } else {
            $builder->where($column, $numberOperator, $value[0], $operand);
        }
    }

    private function prepareValue($numberOperator, array $value): array
    {
        if (
            (isset($value[0]) && ! is_numeric($value[0]))
            || ($numberOperator === 'BETWEEN' && ! $value[1])
        ) {
            throw new CriteriaBuilderException('Value is required.');
        }

        return array_map('intval', $value);
    }
}
