<?php

declare(strict_types=1);

namespace App\Filter\ApiPlatform\Utils;

use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\QueryBuilder;

/**
 * Cette classe est un filtre personnalisé pour les champs de type array
 * Elle recherche des correspondances partielles dans un champ de type array JSON.
 *
 * différences avec le filtre InFilter:
 * - ArrayFieldFilter recherche des correspondances partielles
 * - InFilter utilise la clause SQL IN pour rechercher des correspondances exactes
 * - Format est différent : pour ArrayFieldFilter, le format est un tableau JSON,
 *  pour InFilter, le format est une liste CSV
 */
class ArrayFieldFilter extends AbstractFilter
{
    /**
     * @param array<mixed> $context
     */
    protected function filterProperty(
        string $property,
        mixed $value,
        QueryBuilder $queryBuilder,
        QueryNameGeneratorInterface $queryNameGenerator,
        string $resourceClass,
        ?Operation $operation = null,
        array $context = [],
    ): void {
        if (!$this->isPropertyEnabled($property, $resourceClass)) {
            return;
        }

        // Vérifier si la valeur est déjà un tableau ou un JSON encodé
        $values = json_decode($value, true);

        // Si json_decode échoue ou si la valeur n'est pas un tableau, traiter comme une chaîne simple
        if (json_last_error() !== JSON_ERROR_NONE || !is_array($values)) {
            $values = [$value];
        }

        $orX = $queryBuilder->expr()->orX();
        foreach ($values as $index => $val) {
            $parameterName = $queryNameGenerator->generateParameterName($property.$index); // Générer un nom de paramètre unique
            $orX->add($queryBuilder->expr()->like(sprintf('o.%s', $property), ':'.$parameterName));
            $queryBuilder->setParameter($parameterName, '%'.$val.'%');
        }
        $queryBuilder->andWhere($orX);
    }

    /**
     * @return array<string, mixed>
     */
    public function getDescription(string $resourceClass): array
    {
        if (!$this->properties) {
            return [];
        }

        $description = [];
        foreach ($this->properties as $property => $strategy) {
            $description[$property] = [
                'property' => $property,
                'type' => 'array',
                'required' => false,
                'swagger' => ['description' => "Filter by $property"],
            ];
        }

        return $description;
    }
}
