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

use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormBuilderInterface;

final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private RollingDateConverterInterface $rollingDateConverter,
        private ListAccompanyingPeriodHelper $listAccompanyingPeriodHelper,
        private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
    ) {}

    public function buildForm(FormBuilderInterface $builder)
    {
        $builder
            ->add('calc_date', PickRollingDateType::class, [
                'label' => 'export.list.acp.Date of calculation for associated elements',
                'help' => 'export.list.acp.The associated referree, localisation, and other elements will be valid at this date',
                'required' => true,
            ]);
    }

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

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

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

    public function getFormDefaultData(): array
    {
        return [
            'calc_date' => new RollingDate(RollingDate::T_TODAY),
        ];
    }

    public function getAllowedFormattersTypes()
    {
        return [FormatterInterface::TYPE_LIST];
    }

    public function getDescription()
    {
        return 'export.list.acp.Generate a list of accompanying periods, filtered on different parameters.';
    }

    public function getGroup(): string
    {
        return 'Exports of accompanying courses';
    }

    public function getLabels($key, array $values, $data)
    {
        return $this->listAccompanyingPeriodHelper->getLabels($key, $values, $data);
    }

    public function getQueryKeys($data): array
    {
        return $this->listAccompanyingPeriodHelper->getQueryKeys($data);
    }

    public function getResult($query, $data, ExportGenerationContext $context): array
    {
        return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
    }

    public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
    {
        return 'export.list.acp.List of accompanying periods';
    }

    public function getType(): string
    {
        return Declarations::PERSON_TYPE;
    }

    public function initiateQuery(array $requiredModifiers, array $acl, array $data, ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
    {
        $qb = $this->entityManager->createQueryBuilder();

        $qb
            ->from(AccompanyingPeriod::class, 'acp')
            ->andWhere('acp.step != :list_acp_step')
            ->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT);

        $this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $context->byUser, $data);

        $this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));

        AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);

        $qb
            ->addOrderBy('acp.openingDate')
            ->addOrderBy('acp.closingDate')
            ->addOrderBy('acp.id');

        return $qb;
    }

    public function requiredRole(): string
    {
        return PersonVoter::LISTS;
    }

    public function supportsModifiers()
    {
        return [
            Declarations::ACP_TYPE,
        ];
    }
}
