<?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\PersonBundle\Export\Filter\AccompanyingCourseFilters;

use Chill\MainBundle\Export\ExportDataNormalizerTrait;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Form\Type\PickSocialActionType;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final readonly class SocialActionFilter implements FilterInterface
{
    use ExportDataNormalizerTrait;

    private const PREFIX = 'acp_by_social_action_filter';

    public function __construct(
        private SocialActionRender $actionRender,
        private RollingDateConverterInterface $rollingDateConverter,
        private TranslatorInterface $translator,
        private SocialActionRepository $socialActionRepository,
    ) {}

    public function addRole(): ?string
    {
        return null;
    }

    public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
    {
        $p = self::PREFIX;

        $dql =
            sprintf(
                'SELECT 1 FROM %s acp_by_social_action_filter WHERE acp_by_social_action_filter.accompanyingPeriod = acp ',
                AccompanyingPeriod\AccompanyingPeriodWork::class
            );

        if (0 < count($data['accepted_socialactions'])) {
            $dql .= 'AND acp_by_social_action_filter.socialAction IN (:acp_by_social_action_filter_actions)';
            $qb->setParameter("{$p}_actions", SocialAction::getDescendantsWithThisForActions($data['accepted_socialactions']));
        }

        if (null !== ($data['start_date_after'] ?? null)) {
            $dql .= " AND acp_by_social_action_filter.startDate > :{$p}_start_date_after";
            $qb->setParameter("{$p}_start_date_after", $this->rollingDateConverter->convert($data['start_date_after']));
        }

        if (null !== ($data['start_date_before'] ?? null)) {
            $dql .= " AND acp_by_social_action_filter.startDate <= :{$p}_start_date_before";
            $qb->setParameter("{$p}_start_date_before", $this->rollingDateConverter->convert($data['start_date_before']));
        }

        if (null !== ($data['end_date_after'] ?? null)) {
            $dql .= " AND acp_by_social_action_filter.endDate > :{$p}_end_date_after OR acp_by_social_action_filter.endDate IS NULL";
            $qb->setParameter("{$p}_end_date_after", $this->rollingDateConverter->convert($data['end_date_after']));
        }

        if (null !== ($data['end_date_before'] ?? null)) {
            $dql .= " AND acp_by_social_action_filter.endDate <= :{$p}_end_date_before OR acp_by_social_action_filter.endDate IS NULL";
            $qb->setParameter("{$p}_end_date_before", $this->rollingDateConverter->convert($data['end_date_before']));
        }

        $qb->andWhere($qb->expr()->exists($dql));
    }

    public function applyOn(): string
    {
        return Declarations::ACP_TYPE;
    }

    public function buildForm(FormBuilderInterface $builder): void
    {
        $actions = $this->socialActionRepository->findAllOrdered();

        $builder
            ->add('accepted_socialactions', PickSocialActionType::class, [
                'multiple' => true,
                'label' => 'export.filter.course.by_social_action.Accepted socialactions',
                'help' => 'export.filter.course.by_social_action.accepted socialations help',
                'show_social_issue_parenthesis' => true,
                'show_deactivated' => true,
                'choices' => $actions,
            ])
            ->add('start_date_after', PickRollingDateType::class, [
                'label' => 'export.filter.course.by_social_action.start date after',
                'help' => 'export.filter.course.by_social_action.start date after help',
                'required' => false,
            ])
            ->add('start_date_before', PickRollingDateType::class, [
                'label' => 'export.filter.course.by_social_action.start date before',
                'help' => 'export.filter.course.by_social_action.start date before help',
                'required' => false,
            ])
            ->add('end_date_after', PickRollingDateType::class, [
                'label' => 'export.filter.course.by_social_action.end date after',
                'help' => 'export.filter.course.by_social_action.end date after help',
                'required' => false,
            ])
            ->add('end_date_before', PickRollingDateType::class, [
                'label' => 'export.filter.course.by_social_action.end date before',
                'help' => 'export.filter.course.by_social_action.end date before help',
                'required' => false,
            ])
        ;
    }

    public function getNormalizationVersion(): int
    {
        return 1;
    }

    public function normalizeFormData(array $formData): array
    {
        return ['accepted_socialactions' => $this->normalizeDoctrineEntity($formData['accepted_socialactions']),
            'start_date_after' => $formData['start_date_after']?->normalize(),
            'start_date_before' => $formData['start_date_before']?->normalize(),
            'end_date_after' => $formData['end_date_after']?->normalize(),
            'end_date_before' => $formData['end_date_before']?->normalize()];
    }

    public function denormalizeFormData(array $formData, int $fromVersion): array
    {
        return ['accepted_socialactions' => $this->denormalizeDoctrineEntity($formData['accepted_socialactions'], $this->socialActionRepository),
            'start_date_after' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['start_date_after'] ?? null),
            'start_date_before' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['start_date_before'] ?? null),
            'end_date_after' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['end_date_after'] ?? null),
            'end_date_before' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['end_date_before'] ?? null)];
    }

    public function getFormDefaultData(): array
    {
        return [
            'accepted_social_actions' => [],
            'start_date_after' => null,
            'start_date_before' => null,
            'end_date_after' => null,
            'end_date_before' => null,
        ];
    }

    public function describeAction($data, ExportGenerationContext $context): array
    {
        $actions = [];

        $socialactions = $data['accepted_socialactions'];

        foreach ($socialactions as $action) {
            $actions[] = $this->actionRender->renderString($action, [
                'show_and_children' => true,
            ]);
        }

        return ['export.filter.course.by_social_action.Filtered by socialactions: only %socialactions%', [
            '%socialactions%' => implode(', ', $actions),
            '%start_date_after%' => null === ($data['start_date_after'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['start_date_after'])->format('d-m-Y'),
            '%start_date_before%' => null === ($data['start_date_before'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['start_date_before'])->format('d-m-Y'),
            '%end_date_after%' => null === ($data['end_date_after'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['end_date_after'])->format('d-m-Y'),
            '%end_date_before%' => null === ($data['end_date_before'] ?? null) ? '('.$this->translator->trans('export.filter.course.by_social_action.date ignored').')' : $this->rollingDateConverter->convert($data['end_date_before'])->format('d-m-Y'),
        ]];
    }

    public function getTitle(): string
    {
        return 'export.filter.course.by_social_action.title';
    }
}
