<?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\RestBundle\Worker\UserWs\Version1\UserApplicationRole\Add;

use Sso\RestBundle\Api\ExceptionCollection\MainValidationException;
use Sso\RestBundle\Api\Manager as ApiManager;
use Sso\RestBundle\ModelRest\Request\UserWs\Version1\UserApplicationRole\Add as RestRequest;
use Sso\RestBundle\ModelRest\Response\UserWs\Version1\UserApplicationRole\Add as RestResponse;
use Sso\RestBundle\ModelXml\Request\UserWs\Version1\UserApplicationRole\Add as XmlRequest;
use Sso\RestBundle\ModelXml\Response\UserWs\Version1\UserApplicationRole\Add as XmlResponse;
use FOS\RestBundle\View\View as RestView;
use GuzzleHttp\Exception\RequestException;
use LifeStyle\Tools\RestAuthBundle\Security\Authentication\Token\UidentifyToken;
use LifeStyle\Tools\RestErrorBundle\Api\Error\Errors\Index as Errors;
use LifeStyle\Tools\RestErrorBundle\Api\Manager as ErrorApi;
use Symfony\Component\HttpFoundation\Request;

/**
 * Class Handler
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package    Sso\RestBundle\Worker\UserWs\Version1\UserApplicationRole\Add
 */
class Handler
{
    const STATUS = 'status';
    const ERROR_EXTERNAL = 'external';

    /**
     * @var UidentifyToken
     */
    private $userToken;

    /**
     * @var ApiManager
     */
    private $apiM;

    /**
     * @var ErrorApi
     */
    private $errorApi;

    /**
     * @var Errors
     */
    private $errors;

    /**
     * @var XmlResponse\Error\Response
     */
    private $errorXmlEntity;

    /**
     * @var XmlResponse\Success\Response
     */
    private $successXmlEntity;

    /**
     * @var bool
     */
    private $oneResponseSuccess = false;

    /**
     * holds the complete response errors and success for every item
     * @var \Sso\RestBundle\ModelRest\Response\UserWs\Version1\UserApplicationRole\Delete\Response
     */
    protected $restResponseModel;

    /**
     * @param ApiManager $apiM
     */
    public function __construct(ApiManager $apiM)
    {
        $this->apiM = $apiM;
        $this->client = $this->apiM->client();
        $this->errorApi = $this->apiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->format = 'json';
        $this->userToken = $this->apiM->getContainer()->get('security.token_storage')->getToken();
        $this->restResponseModel = $this->apiM->modelRest()->response()->userWs()->version1()
            ->userApplicationRole()->add()->response();
    }

    /**
     * @param Request $request
     *
     * @return RestView
     */
    public function init(Request $request)
    {
        try {
            list($requestContent, $format) = $this->apiM->mainValidator()->validateRestRequest($request);
        } catch (MainValidationException $exc) {
            $this->errorApi->error()->errors()->setScriptSeconds($this->apiM->scriptTimeSeconds());

            return $this->errorApi->error()->view()->getErrorsView($this->errors->getErrors(), 416);
        }

        /** @var RestRequest\Request $requestModelRest */
        $requestModelRest = $this->apiM->serializer()->deserialize(
            $requestContent,
            RestRequest\Request::class,
            $format
        );

        foreach ($requestModelRest->getRoleTypes() as $roleType) {
            $responseRoleType = $this->send($roleType);
            $this->restResponseModel->addRoleType($responseRoleType);
        }

        $this->restResponseModel->setCode($this->oneResponseSuccess ? 200 : 400);

        return $this->apiM->restView()->setData($this->restResponseModel)
            ->setStatusCode($this->oneResponseSuccess ? 200 : 400);
    }

    /**
     * @param RestRequest\RoleType $requestRoleType
     *
     * @return RestResponse\RoleType
     */
    public function send(RestRequest\RoleType $requestRoleType)
    {
        $requestModelXml = $this->apiM->modelXml()->request()->userWs()->version1()
            ->userApplicationRole()->add()->request();

        $credentialsModelXml = $this->apiM->modelXml()->request()->userWs()->version1()->userApplicationRole()->add()->credentials();
        $credentialsModelXml->setCredentialsData(
            $this->userToken->servicetoken,
            'UserApplicationRoleAdd',
            $this->userToken->useridentifier,
            $this->userToken->username,
            'en'
        );
        $roleModelXml = $this->apiM->modelXml()->request()->userWs()->version1()
            ->userApplicationRole()->add()->userApplicationRole();

        $requestModelXml->setCredentials($credentialsModelXml)
            ->setUserApplicationRole($roleModelXml);

        $roleModelXml->setUserApplicationRoleTypeData(
            $requestRoleType->getUserType()->getIdentifier(),
            $requestRoleType->getApplicationType()->getName(),
            $requestRoleType->getName(),
            $requestRoleType->getActive()
        );

        try {
            $clientResponse = $this->apiM->client()->post(
                $this->apiM->configuration()->userWs()->getBaseUrl()
                . $this->apiM->configuration()->userWs()->getServiceAction(),
                $this->apiM->clientHelper()->options()->getOptions(
                    1,
                    'UserApplicationRole',
                    'Add',
                    $this->apiM->serializer()->serialize($requestModelXml, 'xml')
                )
            );
        } catch (RequestException $ex) {
            $errorResponse = $ex->getResponse()->getBody()->getContents();
            $this->apiM->logger()->error($errorResponse);

            return $this->buildItem([static::STATUS => false, 'xml' => $errorResponse]);
        }

        return $this->buildItem([static::STATUS => true, 'xml' => $clientResponse->getBody()->getContents()]);
    }

    /**
     * @param string $xmlResponse
     *
     * @return RestResponse\RoleType
     */
    private function buildItem($xmlResponse)
    {
        $responseRoleType = $this->apiM->modelRest()->response()->userWs()->version1()
            ->userApplicationRole()->add()->roleType();

        if (!$xmlResponse[static::STATUS]) {
            return $this->buildError($xmlResponse['xml'], $responseRoleType);
        }

        return $this->buildSuccess($xmlResponse['xml'], $responseRoleType);
    }

    /**
     * @param string $xmlString
     * @param RestResponse\RoleType $responseRoleType
     *
     * @return RestResponse\RoleType
     */
    private function buildSuccess($xmlString, RestResponse\RoleType $responseRoleType)
    {
        $this->successXmlEntity = $this->apiM->serializer()->deserialize(
            $xmlString,
            XmlResponse\Success\Response::class,
            'xml'
        );

        if (!$this->successXmlEntity->getUserApplicationRole()
            || ($this->successXmlEntity->getUserApplicationRole()
                && $this->successXmlEntity->getUserApplicationRole()->getAdd()->getStatus() !== 'Success')
        ) {
            return $this->buildError($xmlString, $responseRoleType);
        }

        $this->oneResponseSuccess = true;

        /** @var RestResponse\RoleType $responseRoleType */
        $responseRoleType = $this->apiM->helper()->mapper()->modelInToModelOut(
            $this->successXmlEntity->getUserApplicationRole()->getAdd(),
            $responseRoleType
        );

        $this->restResponseModel
            ->setCode(200)
            ->setTrackId($this->successXmlEntity->getTrackId())
            ->setDate($this->successXmlEntity->getDate())
            ->setScriptTimeSec($this->apiM->scriptTimeSeconds());

        $responseRoleType->setCode(200);

        return $responseRoleType;
    }

    /**
     * @param string $xmlString
     * @param RestResponse\RoleType $responseRoleType
     *
     * @return RestResponse\RoleType
     */
    private function buildError($xmlString, RestResponse\RoleType $responseUserApplicationRoleType)
    {
        $this->errorXmlEntity = $this->apiM->serializer()->deserialize(
            $xmlString,
            XmlResponse\Error\Response::class,
            'xml'
        );
        $errors = $this->errorXmlEntity->getUserApplicationRole()->getAdd()->getErrors();

        foreach ($errors as $error) {
            $errorItem = $this->apiM->modelRest()->response()->userWs()->version1()
                ->userApplicationRole()->add()->error();
            $message = 'ErrorCode: ' . $error->getErrorCode() . ' ErrorRef: '
                . $error->getErrorRef() . ' ShortMessage: ' . $error->getShortMessage();
            $errorItem->setMessage($message);
            $errorItem->setCode(400);
            $errorItem->setException(static::ERROR_EXTERNAL);
            $responseUserApplicationRoleType->addError($errorItem);
        }

        $this->restResponseModel
            ->setCode(400)
            ->setTrackId($this->errorXmlEntity->getTrackId())
            ->setTrackId($this->errorXmlEntity->getTrackId())
            ->setDate($this->errorXmlEntity->getDate())
            ->setScriptTimeSec($this->apiM->scriptTimeSeconds());

        $responseUserApplicationRoleType->setCode(400);

        return $responseUserApplicationRoleType;
    }
}
