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

use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\MainBundle\Templating\Widget\WidgetInterface;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Twig\Environment;

/**
 * add a widget with person list.
 *
 * The configuration is defined by `PersonListWidgetFactory`
 *
 * If the options 'custom_fields' is used, the custom fields entity will be
 * queried from the db and transmitted to the view under the `customFields` variable.
 */
class PersonListWidget implements WidgetInterface
{
    protected UserInterface $user;

    public function __construct(protected PersonRepository $personRepository, protected EntityManagerInterface $entityManager, protected AuthorizationHelperInterface $authorizationHelper, protected TokenStorageInterface $tokenStorage) {}

    public function render(Environment $env, $place, array $context, array $config)
    {
        $numberOfItems = $config['number_of_items'] ?? 20;

        $qb = $this->personRepository
            ->createQueryBuilder('person');

        // show only the person from the authorized centers
        $and = $qb->expr()->andX();
        $centers = $this->authorizationHelper
            ->getReachableCenters($this->getUser(), PersonVoter::SEE);
        $and->add($qb->expr()->in('person.center', ':centers'));
        $qb->setParameter('centers', $centers);

        // add the "only active" query
        if (\array_key_exists('only_active', $config) && true === $config['only_active']) {
            $qb->join('person.accompanyingPeriods', 'ap');
            $or = new Expr\Orx();
            // add the case where closingDate IS NULL
            $andWhenClosingDateIsNull = new Expr\Andx();
            $andWhenClosingDateIsNull->add((new Expr())->isNull('ap.closingDate'));
            $andWhenClosingDateIsNull->add((new Expr())->gte(':now', 'ap.openingDate'));
            $or->add($andWhenClosingDateIsNull);
            // add the case when closingDate is in the future
            $or->add(
                (new Expr())->between(':now', 'ap.openingDate', 'ap.closingDate')
            );
            $and->add($or);
            $qb->setParameter('now', new \DateTime(), Types::DATE_MUTABLE);
        }

        if (\array_key_exists('filtering_class', $config) && null !== $config['filtering_class']) {
            $filteringClass = new $config['filtering_class']();

            if (!$filteringClass instanceof PersonListWidget\PersonFilteringInterface) {
                throw new \UnexpectedValueException(sprintf('the class %s does not implements %s', $config['filtering_class'], PersonListWidget\PersonFilteringInterface::class));
            }
            $ids = $filteringClass->getPersonIds(
                $this->entityManager,
                $this->getUser()
            );
            $in = (new Expr())->in('person.id', ':ids');
            $and->add($in);
            $qb->setParameter('ids', $ids);
        }

        // adding the where clause to the query
        $qb->where($and);

        // ordering the query by lastname, firstname
        $qb->addOrderBy('person.lastName', 'ASC')
            ->addOrderBy('person.firstName', 'ASC');

        $qb->setFirstResult(0)->setMaxResults($numberOfItems);

        $persons = $qb->getQuery()->getResult();

        // get some custom field when the view is overriden and we want to
        // show some custom field in the overriden view.
        $cfields = [];

        if (isset($config['custom_fields'])) {
            if (\count($config['custom_fields']) > 0) {
                $cfs = $this->entityManager
                    ->getRepository(\Chill\CustomFieldsBundle\Entity\CustomField::class)
                    ->findBy(['slug' => $config['custom_fields']]);
                // store the custom fields in a array
                foreach ($cfs as $cf) {
                    $cfields[$cf->getSlug()] = $cf;
                }
            }
        }

        return $env->render(
            '@ChillPerson/Widget/homepage_person_list.html.twig',
            [
                'persons' => $persons,
                'customFields' => $cfields,
            ]
        );
    }

    private function getUser(): UserInterface
    {
        $token = $this->tokenStorage->getToken();

        if (null === $token) {
            throw new \RuntimeException('the token should not be null');
        }

        $user = $token->getUser();

        if (null === $user) {
            throw new \RuntimeException('The user should implement UserInterface. Are you logged in ?');
        }

        return $user;
    }
}
