<?php

declare(strict_types=1);

namespace App\Service\Doctrine;

use App\Filter\Doctrine\TimeConstraint\AbstractTimeFilter;
use App\Filter\Doctrine\TimeConstraint\ActivityYearFilter;
use App\Filter\Doctrine\TimeConstraint\DatetimeFilter;
use App\Service\Constraint\ActivityYearConstraint;
use App\Service\Constraint\DateTimeConstraint;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\FilterCollection;

/**
 * Service de configuration des filtres doctrine.
 *
 * @see src/EventListener/OnKernelRequestPreRead.php
 */
class FiltersConfigurationService
{
    /**
     * L'état du des filtres de contrainte de temps avant l'utilisation de la méthode suspendTimeConstraintFilters()
     * Si $previousTimeConstraintState est `true`, les filtres étaient activés, et si c'est `false`, les filtres
     * étaient désactivés. Si les filtres ne sont pas suspendus, $previousTimeConstraintState est null.
     */
    protected ?bool $previousTimeConstraintState = null;

    public function __construct(
        private EntityManagerInterface $entityManager,
        private DateTimeConstraint $dateTimeConstraint,
        private ActivityYearConstraint $activityYearConstraint,
    ) {
    }

    /**
     * Return the existing Doctrine SQLFilters.
     */
    protected function getFilters(): FilterCollection
    {
        return $this->entityManager->getFilters();
    }

    /**
     * Enable and preconfigure the doctrine sql filters that apply the time constraints.
     */
    public function configureTimeConstraintFilters(int $accessId): void
    {
        /** @var DatetimeFilter $dateTimeFilter */
        $dateTimeFilter = $this->getFilters()->enable('date_time_filter');
        $dateTimeFilter->setAccessId($accessId);
        $dateTimeFilter->setTimeConstraint($this->dateTimeConstraint);

        /** @var ActivityYearFilter $activityYearFilter */
        $activityYearFilter = $this->getFilters()->enable('activity_year_filter');
        $activityYearFilter->setAccessId($accessId);
        $activityYearFilter->setTimeConstraint($this->activityYearConstraint);
    }

    /**
     * Suspends les contraintes de temps. Celles-ci pourront être restorées grâce à la méthode
     * restoreTimeConstraintFilters().
     *
     * @see src/Filter/Doctrine/TimeConstraint/AbstractTimeFilter.php
     */
    public function suspendTimeConstraintFilters(): void
    {
        if ($this->previousTimeConstraintState !== null) {
            throw new \RuntimeException('time constraints is already suspended');
        }

        if ($this->timeFiltersAlreadyDisabled()) {
            $this->previousTimeConstraintState = false;

            return;
        }

        $this->disableFilter('date_time_filter');
        $this->disableFilter('activity_year_filter');

        $this->previousTimeConstraintState = true;
    }

    /**
     * Test si les filtres de temps sont déjà désactivés.
     */
    protected function timeFiltersAlreadyDisabled(): bool
    {
        $filters = $this->getFilters();

        return !$filters->isEnabled('date_time_filter') && !$filters->isEnabled('activity_year_filter');
    }

    /**
     * Suspend le filtre.
     *
     * @param string $name : nom du filtre à suspendre
     */
    protected function disableFilter(string $name): void
    {
        $filters = $this->getFilters();
        /** @var AbstractTimeFilter $dateTimeFilter */
        $dateTimeFilter = $filters->getFilter($name);
        $dateTimeFilter->disable();
    }

    /**
     * Restore les contraintes de temps après qu'elles aient été suspendues au moyen de la
     * méthode suspendTimeConstraintFilters().
     *
     * @see src/Filter/Doctrine/TimeConstraint/AbstractTimeFilter.php
     */
    public function restoreTimeConstraintFilters(): void
    {
        if ($this->previousTimeConstraintState === null) {
            throw new \RuntimeException('time constraints has not been suspended, can not be restored');
        }

        $this->enableFilter('date_time_filter');
        $this->enableFilter('activity_year_filter');

        $this->previousTimeConstraintState = null;
    }

    /**
     * Restaure le filtre.
     *
     * @return void
     */
    protected function enableFilter(string $name)
    {
        $filters = $this->getFilters();
        /** @var AbstractTimeFilter $dateTimeFilter */
        $dateTimeFilter = $filters->getFilter($name);
        $dateTimeFilter->enable();
    }
}
