<?php

declare(strict_types=1);

namespace App\Module\Commercial\Domain\Repository;

use App\Module\Commercial\Domain\Entity\Client;
use Doctrine\ORM\QueryBuilder;

interface ClientRepositoryInterface
{
    public function findById(int $id): ?Client;

    public function save(Client $client): void;

    /**
     * Construit un QueryBuilder de liste pour le repertoire clients.
     * - Exclut toujours les clients soft-deletes (deleted_at IS NOT NULL, RG-1.24).
     * - Archivage (RG-1.25) :
     *     - $archivedOnly = true  -> uniquement les archives (is_archived = true) ;
     *     - sinon $includeArchived = true -> actifs + archives (echappatoire) ;
     *     - sinon (defaut) -> uniquement les actifs (is_archived = false).
     *   $archivedOnly a la priorite sur $includeArchived.
     * - Tri par defaut : companyName ASC (RG-1.26).
     * - $search : recherche fuzzy insensible a la casse sur companyName +
     *   lastName + email (metacaracteres LIKE echappes). Ignore si null/vide.
     * - $categoryCodes : restreint aux clients possedant au moins une categorie
     *   dont le code est dans la liste (OR — ERP-78). Liste vide = pas de filtre.
     * - $siteIds : restreint aux clients ayant au moins une adresse rattachee a
     *   l'un des sites donnes (OR — RG-1.10). Liste vide = pas de filtre.
     *
     * Filtrage centralise ICI (et non dans les providers/controllers) pour que
     * la liste paginee (ClientProvider) et l'export (ClientExportController)
     * partagent strictement la meme logique de selection.
     *
     * Contrat = SELECTION uniquement (filtres + tri). Aucun fetch-join to-many :
     * l'hydratation des collections affichees est une decision de l'appelant
     * (cf. {@see self::hydrateListCollections()}), pour ne pas imposer le cout
     * d'un produit cartesien a un consommateur qui ne filtrerait/compterait que
     * (ERP-100).
     *
     * @param list<string> $categoryCodes
     * @param list<int>    $siteIds
     */
    public function createListQueryBuilder(
        bool $includeArchived = false,
        ?string $search = null,
        array $categoryCodes = [],
        array $siteIds = [],
        bool $archivedOnly = false,
    ): QueryBuilder;

    /**
     * Hydrate en lot les collections affichees par le repertoire (categories,
     * adresses et leurs sites) sur un jeu de clients DEJA charges, via l'identity
     * map Doctrine (memes instances). A appeler apres une selection bornee (page
     * courante ou jeu d'export) pour eviter le N+1 a la serialisation, sans
     * imposer de fetch-join au QueryBuilder de selection (ERP-100).
     *
     * Charge les categories et les adresses/sites en DEUX requetes distinctes
     * (et non un triple fetch-join) pour ne pas multiplier categories x adresses
     * x sites en un seul produit cartesien.
     *
     * @param list<Client> $clients
     */
    public function hydrateListCollections(array $clients): void;
}
