<?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\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final readonly class RequestorFilter implements FilterInterface
{
    private const DEFAULT_CHOICE = 'participation';

    private const REQUESTOR_CHOICES = [
        'is person concerned' => 'participation',
        'is other person' => 'other_person',
        'is thirdparty' => 'thirdparty',
        'no requestor' => 'no_requestor',
    ];

    public function __construct(private TranslatorInterface $translator, private EntityManagerInterface $em) {}

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

    public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
    {
        $where = $qb->getDQLPart('where');

        switch ($data['accepted_choices']) {
            case 'participation':
                if (!\in_array('acppart', $qb->getAllAliases(), true)) {
                    $qb->join('acp.participations', 'acppart');
                }

                $clause = $qb->expr()->andX(
                    $qb->expr()->isNotNull('acp.requestorPerson'),
                    $qb->expr()->eq('acp.requestorPerson', 'acppart.person')
                );

                break;

            case 'other_person':
                $expr = $this->em->getExpressionBuilder();

                $clause = $expr->andX(
                    $expr->isNotNull('acp.requestorPerson'),
                    $expr->notIn(
                        'acp.requestorPerson',

                        // subquery
                        $this->em->createQueryBuilder()
                            ->from('ChillPersonBundle:AccompanyingPeriod', 'acp2')
                            ->join('acp2.participations', 'acppart2')
                            ->select('identity(acp2.requestorPerson)')
                            ->where($expr->eq('acp2.requestorPerson', 'acppart2.person'))
                            ->andWhere('acp2.id = acp.id')
                            ->getDQL()
                    )
                );

                break;

            case 'thirdparty':
                $clause = $qb->expr()->isNotNull('acp.requestorThirdParty');

                break;

            case 'no_requestor':
                $clause = $qb->expr()->andX(
                    $qb->expr()->isNull('acp.requestorPerson'),
                    $qb->expr()->isNull('acp.requestorThirdParty')
                );

                break;

            default:
                throw new \Exception('Uncaught choice exception');
        }

        if ($where instanceof Andx) {
            $where->add($clause);
        } else {
            $where = $qb->expr()->andX($clause);
        }

        $qb->add('where', $where);
    }

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

    public function buildForm(FormBuilderInterface $builder): void
    {
        $builder->add('accepted_choices', ChoiceType::class, [
            'choices' => self::REQUESTOR_CHOICES,
            'multiple' => false,
            'expanded' => true,
            'empty_data' => self::DEFAULT_CHOICE,
        ]);
    }

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

    public function normalizeFormData(array $formData): array
    {
        return ['accepted_choices' => $formData['accepted_choices']];
    }

    public function denormalizeFormData(array $formData, int $fromVersion): array
    {
        return ['accepted_choices' => $formData['accepted_choices']];
    }

    public function getFormDefaultData(): array
    {
        return ['accepted_choices' => self::DEFAULT_CHOICE];
    }

    public function describeAction($data, ExportGenerationContext $context): array
    {
        $choice = array_flip(self::REQUESTOR_CHOICES)[$data['accepted_choices']];

        return ['Filtered by requestor: only %choice%', [
            '%choice%' => $this->translator->trans($choice),
        ]];
    }

    public function getTitle(): string
    {
        return 'Filter by requestor';
    }
}
