<?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  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\WebserviceBundle\Services\UserSearch\Advanced\Request\Version3;

use JMS\Serializer\SerializerInterface;
use Sso\WebserviceBundle\ErrorHandler\ErrorHandlerInterface;
use Sso\WebserviceBundle\Services\UserSearch\Advanced\RequestParserInterface;
use Sso\WebserviceBundle\Services\UserSearch\Advanced\RequestData\Factory as RequestDataFactory;
use Sso\WebserviceBundle\Services\UserSearch\Advanced\RequestData;
use InvalidArgumentException;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Sso\WebserviceBundle\Services\UserSearch\Advanced\Request\Version3 as RequestModel;

/**
 * Class RequestParser
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package Sso\WebserviceBundle\Services\UserSearch\Advanced\Request\Version3
 */
final class RequestParser implements RequestParserInterface
{
    const NESTED_LEVEL_MAX = 5;

    /**
     * @var SerializerInterface
     */
    private $serializer;

    /**
     * @var RequestDataFactory
     */
    private $factory;

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

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

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

    /**
     * @param string $content
     * @return \Sso\WebserviceBundle\Services\UserSearch\Advanced\RequestData\UserSearch
     */
    public function parse($content)
    {
        /** @var Request $request */
        $request = $this->serializer->deserialize($content, Request::class, 'xml');

        if (!($request instanceof Request)) {
            throw new InvalidArgumentException();
        }

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

        return $this->mapRequest($request);
    }

    /**
     * @param Request $request
     * @return bool
     */
    private function validate(Request $request)
    {
        $validationErrors = $this->validator->validate($request);
        if (count($validationErrors) === 0) {
            return true;
        }
        foreach ($validationErrors as $error) {
            $message = $error->getPropertyPath().': '.$error->getMessage();
            $this->errorHandler->addError(400, 'usa', 'rp110', $error->getMessage(), $message);
        }

        return false;
    }

    /**
     * @param Request $request
     * @return RequestData\UserSearch
     */
    private function mapRequest(RequestModel\Request $request)
    {
        $userSearchDTO = $this->factory->userSearch();

        $userSearch = $request->getUserSearch()->getAdvanced();
        $userSearchDTO->setOffset($userSearch->getOffset());
        $userSearchDTO->setLimit($userSearch->getLimit());
        $userSearchDTO->setFullResponse($userSearch->getFullResponse());

        if (strlen($userSearch->getOrderBy())) {
            $userSearchDTO->setOrderBy($userSearch->getOrderBy());
            $userSearchDTO->setOrderDir('DESC' === strtoupper($userSearch->getOrderDir()) ? 'DESC' : 'ASC');
        }

        if (null !== $userSearch->getFilters()) {
            foreach ($userSearch->getFilters()->getFilter() as $filter) {
                $userSearchDTO->addFilter($this->mapFilter($filter));
            }
        }

        return $userSearchDTO;
    }

    /**
     * @param RequestModel\Filter $filter
     * @param int $nestedLevel
     * @return RequestData\Filter
     * @throws \Exception
     */
    private function mapFilter(RequestModel\Filter $filter, $nestedLevel = 0)
    {
        if (static::NESTED_LEVEL_MAX < $nestedLevel) {
            throw new \Exception(sprintf('Maximum nested level %d reached!', static::NESTED_LEVEL_MAX));
        }

        $filterDTO = $this->factory->filter();
        $filterDTO->setStrategy($filter->getStrategy());
        $filterDTO->setOperator($filter->getOperator());

        if (null !== $filter->getUserType()) {
            $filterDTO->setUserType($this->mapUserType($filter->getUserType()));
        } elseif (null !== $filter->getApplicationType()) {
            $filterDTO->setApplicationType($this->mapApplicationType($filter->getApplicationType()));
        } elseif (null !== $filter->getFilters()) {
            foreach ($filter->getFilters()->getFilter() as $filter) {
                $filterDTO->addFilter($this->mapFilter($filter, $nestedLevel + 1));
            }
        }

        return $filterDTO;
    }

    /**
     * @param UserType $userType
     * @return RequestData\UserType
     */
    private function mapUserType(RequestModel\UserType $userType)
    {
        $userTypeDTO = $this->factory->userType();

        $userTypeDTO->setGuid($userType->getGuid());
        $userTypeDTO->setEmail($userType->getEmail());
        $userTypeDTO->setUsername($userType->getUsername());
        $userTypeDTO->setFirstname($userType->getFirstname());
        $userTypeDTO->setLastname($userType->getLastname());
        $userTypeDTO->setActive($userType->getActive());
        $userTypeDTO->setDeleted($userType->getDeleted());
        $userTypeDTO->setMfaEnabled($userType->getMfaEnabled());
        $userTypeDTO->setCreatedAt($userType->getCreatedAt());
        $userTypeDTO->setUpdatedAt($userType->getUpdatedAt());
        $userTypeDTO->setLastLoginAt($userType->getLastLoginAt());

        return $userTypeDTO;
    }

    /**
     * @param ApplicationType $appType
     * @return RequestData\ApplicationType
     */
    private function mapApplicationType(RequestModel\ApplicationType $appType)
    {
        $appTypeDTO = $this->factory->applicationType();
        $appTypeDTO->setName($appType->getName());
        $appTypeDTO->setActive($appType->getActive());

        if (null !== $appType->getAttributeType()) {
            $appTypeDTO->setAttributeType($this->mapAttributeType($appType->getAttributeType()));
        }

        if (null !== $appType->getRoleType()) {
            $appTypeDTO->setRoleType($this->mapRoleType($appType->getRoleType()));
        }

        return $appTypeDTO;
    }

    /**
     * @param AttributeType $attributeType
     * @return RequestData\AttributeType
     */
    private function mapAttributeType(RequestModel\AttributeType $attributeType)
    {
        $attTypeDTO = $this->factory->attributeType();
        $attTypeDTO->setName($attributeType->getName());
        $attTypeDTO->setValue($attributeType->getValue());
        $attTypeDTO->setActive($attributeType->getActive());

        return $attTypeDTO;
    }

    /**
     * @param RoleType $roleType
     * @return RequestData\RoleType
     */
    private function mapRoleType(RequestModel\RoleType $roleType)
    {
        $roleTypeDTO = $this->factory->roleType();
        $roleTypeDTO->setName($roleType->getName());
        $roleTypeDTO->setActive($roleType->getActive());

        return $roleTypeDTO;
    }
}
