<?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\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Templating\Entity\AddressRender;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;

class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface
{
    use NormalizerAwareTrait;

    private const NULL_POSTCODE_COUNTRY = [
        'id', 'name', 'code',
    ];

    private const NULL_VALUE = [
        'address_id',
        'text',
        'street',
        'streetNumber',
        'postcode',
        'country',
        'floor',
        'corridor',
        'steps',
        'flat',
        'buildingName',
        'distribution',
        'extra',
        'validFrom' => \DateTimeInterface::class,
        'validTo' => \DateTimeInterface::class,
        'confidential',
    ];

    public function __construct(private readonly AddressRender $addressRender) {}

    /**
     * @param Address     $address
     * @param string|null $format
     */
    public function normalize($address, $format = null, array $context = [])
    {
        if ($address instanceof Address) {
            $data = [
                'address_id' => $address->getId(),
                'text' => $this->addressRender->renderString($address, []),
                'street' => $address->getStreet(),
                'streetNumber' => $address->getStreetNumber(),
                'postcode' => [
                    'id' => $address->getPostCode()->getId(),
                    'name' => $address->getPostCode()->getName(),
                    'code' => $address->getPostCode()->getCode(),
                    'center' => $address->getPostcode()->getCenter(),
                ],
                'country' => [
                    'id' => $address->getPostCode()->getCountry()->getId(),
                    'name' => $address->getPostCode()->getCountry()->getName(),
                    'code' => $address->getPostCode()->getCountry()->getCountryCode(),
                ],
                'floor' => $address->getFloor(),
                'corridor' => $address->getCorridor(),
                'steps' => $address->getSteps(),
                'flat' => $address->getFlat(),
                'buildingName' => $address->getBuildingName(),
                'distribution' => $address->getDistribution(),
                'extra' => $address->getExtra(),
                'confidential' => $address->getConfidential(),
                'lines' => $this->addressRender->renderLines($address),
            ];

            if ('json' === $format) {
                $data['addressReference'] = $this->normalizer->normalize(
                    $address->getAddressReference(),
                    $format,
                    [AbstractNormalizer::GROUPS => ['read']]
                );
                $data['validFrom'] = $address->getValidFrom();
                $data['validTo'] = $address->getValidTo();
                $data['refStatus'] = $address->getRefStatus();
                $data['point'] = $this->normalizer->normalize(
                    $address->getPoint(),
                    $format,
                    [AbstractNormalizer::GROUPS => ['read']]
                );
                $data['isNoAddress'] = $address->isNoAddress();
            } elseif ('docgen' === $format) {
                $dateContext = array_merge($context, ['docgen:expects' => \DateTimeInterface::class]);
                $data['validFrom'] = $this->normalizer->normalize($address->getValidFrom(), $format, $dateContext);
                $data['validTo'] = $this->normalizer->normalize($address->getValidTo(), $format, $dateContext);
            }

            return $data;
        }

        if (null === $address) {
            $helper = new NormalizeNullValueHelper($this->normalizer);

            return array_merge(
                $helper->normalize(self::NULL_VALUE, $format, $context),
                [
                    'postcode' => $helper->normalize(self::NULL_POSTCODE_COUNTRY, $format, $context),
                    'country' => $helper->normalize(self::NULL_POSTCODE_COUNTRY, $format, $context),
                    'lines' => [],
                ]
            );
        }

        throw new UnexpectedValueException();
    }

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

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

        return false;
    }
}
