<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2018 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\WebserviceBundle\Services\UserApplicationAttribute\Add\Request\Version2;

use JMS\Serializer\SerializerInterface;
use JMS\Serializer\Exception\Exception as SerializerException;
use Sso\WebserviceBundle\ErrorHandler\ErrorHandlerInterface;
use Sso\WebserviceBundle\Services\UserApplicationAttribute\Add\RequestData;
use Sso\WebserviceBundle\Services\UserApplicationAttribute\Add\RequestParserInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;

/**
 * Class RequestParser
 *
 * @copyright  2018 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package Sso\WebserviceBundle\Services\UserApplicationAttribute\Add\Request\Version2
 */
final class RequestParser implements RequestParserInterface
{
    /**
     * @var SerializerInterface
     */
    private $serializer;

    /**
     * @var RequestData\Factory
     */
    private $factory;

    /**
     * @var ErrorHandlerInterface
     */
    private $errorHandler;

    /**
     * @var ValidatorInterface
     */
    private $validator;

    /**
     * RequestParser constructor.
     * @param SerializerInterface $serializer
     * @param RequestData\Factory $factory
     * @param ErrorHandlerInterface $errorHandler
     * @param ValidatorInterface $validator
     */
    public function __construct(
        SerializerInterface $serializer,
        RequestData\Factory $factory,
        ErrorHandlerInterface $errorHandler,
        ValidatorInterface $validator
    ) {
        $this->serializer = $serializer;
        $this->factory = $factory;
        $this->errorHandler = $errorHandler;
        $this->validator = $validator;
    }

    /**
     * @param string $content
     * @return RequestData\User
     */
    public function parse(string $content): RequestData\User
    {
        $user = $this->factory->user();

        $request = $this->deserialize($content);
        if (null === $request) {
            return $user;
        }

        if (!$this->validate($request)) {
            return $user;
        }

        $this->setUser($request, $user);
        $this->setApplication($request, $user);
        $this->setAttributes($request, $user);

        return $user;
    }

    /**
     * @param string $content
     * @return Request|null
     */
    private function deserialize(string $content)
    {
        try {
            return $this->serializer->deserialize($content, Request::class, 'xml');
        } catch (SerializerException $exception) {
            $this->errorHandler->addError(
                400,
                'uaaa1',
                'uaaa001',
                'Invalid request body',
                'Request body could not be parsed. Please check request format.'
            );
        }

        return null;
    }

    /**
     * @param Request $request
     * @return bool
     */
    private function validate(Request $request): bool
    {
        $violations = $this->validator->validate($request);
        if (count($violations) === 0) {
            return true;
        }

        foreach ($violations as $error) {
            $message = $error->getPropertyPath() . ': ' . $error->getMessage();
            $this->errorHandler->addError(400, $message, $message, $message, $message);
        }

        return false;
    }

    /**
     * @param Request $request
     * @param RequestData\User $user
     */
    private function setUser(Request $request, RequestData\User $user): void
    {
        $requestUser = $request->getUserApplicationAttribute()->getAdd()->getKey()->getUserType();
        if ($requestUser->hasIdentifier()) {
            $user->setIdentifier($requestUser->getIdentifier());
        }
        if ($requestUser->hasEmail()) {
            $user->setEmail($requestUser->getEmail());
        }
        if ($requestUser->hasGuid()) {
            $user->setGuid($requestUser->getGuid());
        }
        if ($requestUser->hasUsername()) {
            $user->setUsername($requestUser->getUsername());
        }
    }

    /**
     * @param Request $request
     * @param RequestData\User $user
     */
    private function setApplication(Request $request, RequestData\User $user): void
    {
        $application = $this->factory->application();
        $application->setName($request->getUserApplicationAttribute()->getAdd()->getKey()->getApplicationType()->getName());
        $application->setForceAttributeAdd($request->getUserApplicationAttribute()->getAdd()->getValue()->isForceAttributeAdd());

        $user->setApplication($application);
    }

    /**
     * @param Request $request
     * @param RequestData\User $user
     */
    private function setAttributes(Request $request, RequestData\User $user): void
    {
        $requestAttributes = $request->getUserApplicationAttribute()->getAdd()->getValue()->getAttributeTypes();
        foreach ($requestAttributes as $requestAttribute) {
            $attribute = $this->factory->attribute();
            $attribute->setName($requestAttribute->getName());
            $attribute->setType($requestAttribute->getType());
            $attribute->setValue($requestAttribute->getValue());

            $user->getApplication()->addAttribute($attribute);
        }
    }
}
