<?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\Serializer\Normalizer;

use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Templating\Entity\UserRender;
use libphonenumber\PhoneNumber;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;

class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface
{
    use NormalizerAwareTrait;

    final public const AT_DATE = 'chill:user:at_date';

    final public const NULL_USER = [
        'type' => 'user',
        'id' => '',
        'username' => '',
        'text' => '',
        'text_without_absent' => '',
        'label' => '',
        'email' => '',
        'isAbsent' => false,
        'absenceStart' => null,
        'absenceEnd' => null,
        'enabled' => true,
    ];

    public function __construct(private readonly UserRender $userRender, private readonly ClockInterface $clock) {}

    /**
     * @param mixed|null $format
     *
     * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface
     */
    public function normalize($object, $format = null, array $context = [])
    {
        /** @var array{"chill:user:at_date"?: \DateTimeImmutable|\DateTime} $context */
        /** @var User $object */
        $userJobContext = array_merge(
            $context,
            ['docgen:expects' => UserJob::class, 'groups' => 'docgen:read']
        );
        $scopeContext = array_merge(
            $context,
            ['docgen:expects' => Scope::class, 'groups' => 'docgen:read']
        );
        $centerContext = array_merge(
            $context,
            ['docgen:expects' => Center::class, 'groups' => 'docgen:read']
        );
        $locationContext = array_merge(
            $context,
            ['docgen:expects' => Location::class, 'groups' => 'docgen:read']
        );
        $civilityContext = array_merge(
            $context,
            ['docgen:expects' => Civility::class, 'groups' => 'docgen:read']
        );
        $phonenumberContext = array_merge(
            $context,
            ['docgen:expects' => PhoneNumber::class, 'groups' => 'docgen:read']
        );

        $absenceDatesContext = array_merge(
            $context,
            ['docgen:expects' => \DateTimeImmutable::class, 'groups' => 'docgen:read']
        );

        if (null === $object && 'docgen' === $format) {
            return [...self::NULL_USER, 'phonenumber' => $this->normalizer->normalize(null, $format, $phonenumberContext), 'civility' => $this->normalizer->normalize(null, $format, $civilityContext), 'user_job' => $this->normalizer->normalize(null, $format, $userJobContext), 'main_center' => $this->normalizer->normalize(null, $format, $centerContext), 'main_scope' => $this->normalizer->normalize(null, $format, $scopeContext), 'current_location' => $this->normalizer->normalize(null, $format, $locationContext), 'main_location' => $this->normalizer->normalize(null, $format, $locationContext)];
        }

        $at = $context[self::AT_DATE] ?? $this->clock->now();
        if ($at instanceof \DateTime) {
            $at = \DateTimeImmutable::createFromMutable($at);
        }

        $data = [
            'type' => 'user',
            'id' => $object->getId(),
            'username' => $object->getUsername(),
            'text' => $this->userRender->renderString($object, ['at_date' => $at]),
            'text_without_absent' => $this->userRender->renderString($object, ['absence' => false]),
            'label' => $object->getLabel(),
            'email' => (string) $object->getEmail(),
            'phonenumber' => $this->normalizer->normalize($object->getPhonenumber(), $format, $phonenumberContext),
            'user_job' => $this->normalizer->normalize($object->getUserJob($at), $format, $userJobContext),
            'main_center' => $this->normalizer->normalize($object->getMainCenter(), $format, $centerContext),
            'main_scope' => $this->normalizer->normalize($object->getMainScope($at), $format, $scopeContext),
            'isAbsent' => $object->isAbsent(),
            'absenceStart' => $this->normalizer->normalize($object->getAbsenceStart(), $format, $absenceDatesContext),
            'absenceEnd' => $this->normalizer->normalize($object->getAbsenceEnd(), $format, $absenceDatesContext),
            'enabled' => $object->isEnabled(),
        ];

        if ('docgen' === $format) {
            $data['civility'] = $this->normalizer->normalize($object->getCivility(), $format, $civilityContext);
            $data['current_location'] = $this->normalizer->normalize($object->getCurrentLocation(), $format, $locationContext);
            $data['main_location'] = $this->normalizer->normalize($object->getMainLocation(), $format, $locationContext);
        }

        return $data;
    }

    public function supportsNormalization($data, $format = null, array $context = []): bool
    {
        if ($data instanceof User && ('json' === $format || 'docgen' === $format)) {
            return true;
        }

        if (null === $data && 'docgen' === $format && User::class === ($context['docgen:expects'] ?? null)) {
            return true;
        }

        return false;
    }
}
