<?php

/*
 * This file is part of the FOSRestBundle package.
 *
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace FOS\RestBundle\Serializer\Normalizer;

@trigger_error(sprintf('The %s\ExceptionHandler class is deprecated since FOSRestBundle 2.8.', __NAMESPACE__), E_USER_DEPRECATED);

use JMS\Serializer\Context;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\XmlSerializationVisitor;

/**
 * @deprecated since 2.8
 */
class ExceptionHandler extends AbstractExceptionNormalizer implements SubscribingHandlerInterface
{
    public static function getSubscribingMethods()
    {
        return [
            [
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => \Error::class,
                'method' => 'serializeErrorToJson',
            ],
            [
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
                'format' => 'json',
                'type' => \Exception::class,
                'method' => 'serializeToJson',
            ],
            [
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
                'format' => 'xml',
                'type' => \Error::class,
                'method' => 'serializeErrorToXml',
            ],
            [
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
                'format' => 'xml',
                'type' => \Exception::class,
                'method' => 'serializeToXml',
            ],
        ];
    }

    public function serializeToJson(
        JsonSerializationVisitor $visitor,
        \Exception $exception,
        array $type,
        Context $context
    ) {
        $data = $this->convertToArray($exception, $context);

        return $visitor->visitArray($data, $type, $context);
    }

    public function serializeErrorToJson(
        JsonSerializationVisitor $visitor,
        \Throwable $exception,
        array $type,
        Context $context
    ) {
        $data = $this->convertThrowableToArray($exception, $context);

        return $visitor->visitArray($data, $type, $context);
    }

    public function serializeToXml(
        XmlSerializationVisitor $visitor,
        \Exception $exception,
        array $type,
        Context $context
    ) {
        $data = $this->convertToArray($exception, $context);

        $document = $visitor->getDocument(true);

        if (!$visitor->getCurrentNode()) {
            $visitor->createRoot();
        }

        foreach ($data as $key => $value) {
            $entryNode = $document->createElement($key);
            $visitor->getCurrentNode()->appendChild($entryNode);
            $visitor->setCurrentNode($entryNode);

            $node = $context->getNavigator()->accept($value, null, $context);
            if (null !== $node) {
                $visitor->getCurrentNode()->appendChild($node);
            }

            $visitor->revertCurrentNode();
        }
    }

    public function serializeErrorToXml(
        XmlSerializationVisitor $visitor,
        \Throwable $exception,
        array $type,
        Context $context
    ) {
        $data = $this->convertThrowableToArray($exception, $context);

        $document = $visitor->getDocument(true);

        if (!$visitor->getCurrentNode()) {
            $visitor->createRoot();
        }

        foreach ($data as $key => $value) {
            $entryNode = $document->createElement($key);
            $visitor->getCurrentNode()->appendChild($entryNode);
            $visitor->setCurrentNode($entryNode);

            $node = $context->getNavigator()->accept($value, null, $context);
            if (null !== $node) {
                $visitor->getCurrentNode()->appendChild($node);
            }

            $visitor->revertCurrentNode();
        }
    }

    protected function convertToArray(\Exception $exception, Context $context)
    {
        return $this->convertThrowableToArray($exception, $context);
    }

    private function convertThrowableToArray(\Throwable $throwable, Context $context): array
    {
        $data = [];

        if ($context->hasAttribute('template_data')) {
            $templateData = $context->getAttribute('template_data');

            if (array_key_exists('status_code', $templateData)) {
                $data['code'] = $statusCode = $templateData['status_code'];
            } elseif ($context->hasAttribute('status_code')) {
                $data['code'] = $context->getAttribute('status_code');
            }
        } elseif ($context->hasAttribute('status_code')) {
            $data['code'] = $context->getAttribute('status_code');
        }

        $data['message'] = $this->getMessageFromThrowable($throwable, isset($statusCode) ? $statusCode : null);

        return $data;
    }
}
