<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Templating\Listing;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\Listing\FilterOrderType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;

final class FilterOrderHelper
{
    private array $checkboxes = [];

    /**
     * @var array<string, array{label: string}>
     */
    private array $singleCheckbox = [];

    private array $dateRanges = [];

    public const FORM_NAME = 'f';

    private array $formOptions = [];

    private string $formType = FilterOrderType::class;

    private ?array $searchBoxFields = null;

    private ?array $submitted = null;

    /**
     * @var array<string, array{label: string, choices: array, options: array}>
     */
    private array $entityChoices = [];

    /**
     * @var array<string, array{label: string, options: array}>
     */
    private array $userPickers = [];

    public function __construct(
        private readonly FormFactoryInterface $formFactory,
        private readonly RequestStack $requestStack,
    ) {}

    public function addSingleCheckbox(string $name, string $label): self
    {
        $this->singleCheckbox[$name] = ['label' => $label];

        return $this;
    }

    /**
     * @param class-string $class
     */
    public function addEntityChoice(string $name, string $class, string $label, array $choices, array $options = []): self
    {
        $this->entityChoices[$name] = ['label' => $label, 'class' => $class, 'choices' => $choices, 'options' => $options];

        return $this;
    }

    public function getEntityChoices(): array
    {
        return $this->entityChoices;
    }

    public function addUserPicker(string $name, ?string $label = null, array $options = []): self
    {
        $this->userPickers[$name] = ['label' => $label, 'options' => $options];

        return $this;
    }

    public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = [], array $options = []): self
    {
        if ([] === $trans) {
            $trans = $choices;
        }

        $this->checkboxes[$name] = [
            'choices' => $choices,
            'default' => $default,
            'trans' => $trans,
            ...$options,
        ];

        return $this;
    }

    public function addDateRange(string $name, ?string $label = null, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null): self
    {
        $this->dateRanges[$name] = ['from' => $from, 'to' => $to, 'label' => $label];

        return $this;
    }

    public function buildForm(): FormInterface
    {
        return $this->formFactory
            ->createNamed(self::FORM_NAME, $this->formType, $this->getDefaultData(), \array_merge([
                'helper' => $this,
                'method' => 'GET',
                'csrf_protection' => false,
            ], $this->formOptions))
            ->handleRequest($this->requestStack->getCurrentRequest());
    }

    public function getUserPickers(): array
    {
        return $this->userPickers;
    }

    /**
     * @return list<User>
     */
    public function getUserPickerData(string $name): array
    {
        return $this->getFormData()['user_pickers'][$name];
    }

    public function hasCheckboxData(string $name): bool
    {
        return array_key_exists($name, $this->checkboxes);
    }

    public function getCheckboxData(string $name): array
    {
        return $this->getFormData()['checkboxes'][$name];
    }

    public function hasSingleCheckboxData(string $name): bool
    {
        return array_key_exists($name, $this->singleCheckbox);
    }

    public function getSingleCheckboxData(string $name): ?bool
    {
        return $this->getFormData()['single_checkboxes'][$name];
    }

    public function hasEntityChoice(string $name): bool
    {
        return array_key_exists($name, $this->entityChoices);
    }

    public function getEntityChoiceData($name): mixed
    {
        return $this->getFormData()['entity_choices'][$name];
    }

    public function getCheckboxes(): array
    {
        return $this->checkboxes;
    }

    public function hasCheckBox(string $name): bool
    {
        return array_key_exists($name, $this->checkboxes);
    }

    /**
     * @return array<string, array{label: string}>
     */
    public function getSingleCheckbox(): array
    {
        return $this->singleCheckbox;
    }

    public function hasDateRangeData(string $name): bool
    {
        return array_key_exists($name, $this->dateRanges);
    }

    /**
     * @return array{to: ?\DateTimeImmutable, from: ?\DateTimeImmutable}
     */
    public function getDateRangeData(string $name): array
    {
        return $this->getFormData()['dateRanges'][$name];
    }

    public function getDateRanges(): array
    {
        return $this->dateRanges;
    }

    public function getQueryString(): ?string
    {
        return $this->getFormData()['q'];
    }

    public function hasSearchBox(): bool
    {
        return null !== $this->searchBoxFields;
    }

    public function setSearchBox(?array $searchBoxFields = null): self
    {
        $this->searchBoxFields = $searchBoxFields;

        return $this;
    }

    private function getDefaultData(): array
    {
        $r = [
            'checkboxes' => [],
            'dateRanges' => [],
            'single_checkboxes' => [],
            'entity_choices' => [],
            'user_pickers' => [],
        ];

        if ($this->hasSearchBox()) {
            $r['q'] = '';
        }

        foreach ($this->checkboxes as $name => $c) {
            $r['checkboxes'][$name] = $c['default'];
        }

        foreach ($this->dateRanges as $name => $defaults) {
            $r['dateRanges'][$name]['from'] = $defaults['from'];
            $r['dateRanges'][$name]['to'] = $defaults['to'];
        }

        foreach ($this->singleCheckbox as $name => $c) {
            $r['single_checkboxes'][$name] = false;
        }

        foreach ($this->entityChoices as $name => $c) {
            $r['entity_choices'][$name] = ($c['options']['multiple'] ?? true) ? [] : null;
        }

        foreach ($this->userPickers as $name => $u) {
            $r['user_pickers'][$name] = ($u['options']['multiple'] ?? true) ? [] : null;
        }

        return $r;
    }

    private function getFormData(): array
    {
        if (null === $this->submitted) {
            $this->submitted = $this->buildForm()
                ->getData();
        }

        return $this->submitted;
    }
}
