<?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\Version2\UserApplicationRole\Show;

use Sso\RestBundle\Api\Manager as ApiManager;
use Sso\RestBundle\ModelRest\Request\UserWs\Version2\UserApplicationRole\Show\Request as RequestModel;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplicationRole\Show as ResponseShow;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplicationRole\Show\Response as RestResponseModel;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\UserApplicationRole\Show\UserType as ResponseUserType;
use Sso\RestBundle\ModelXml\Response\UserWs\Version2\UserApplicationRole\Show\Error\Response as ErrorXMLResponse;
use Sso\RestBundle\ModelXml\Response\UserWs\Version2\UserApplicationRole\Show\Success\Response as SuccessXMLResponse;
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
 * @package Sso\RestBundle\Worker\UserWs\Version2\UserApplicationRole\Show
 */
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 ErrorXMLResponse
     */
    private $errorXmlEntity;

    /**
     * @var SuccessXMLResponse
     */
    private $successXmlEntity;

    /**
     * @var RestResponseModel
     */
    private $restResponseModel;

    /**
     * @param ApiManager $apiM
     */
    public function __construct(ApiManager $apiM)
    {
        $this->apiM = $apiM;
        $this->errorApi = $this->apiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->restResponseModel = $this->apiM->modelRest()->response()->userWs()->version2()->userApplicationRole()->show()->response();
        $this->userToken = $this->apiM->getToken();
    }

    /**
     * @param Request $request
     * @return \FOS\RestBundle\View\View
     */
    public function init(Request $request)
    {
        // first: validate input data
        $requestModelRest = $this->validateRestRequest($request);

        if ($this->errors->hasErrors()) {
            return $this->errorApi->error()->view()->getErrorsView($this->errors->getErrors());
        }

        // second: send data to xml ws
        $responseUserType = $this->sendXmlRequest(
            $requestModelRest->getIdentifier(),
            $requestModelRest->getApplicationName()
        );
        $this->restResponseModel->addUserType($responseUserType);

        return $this->apiM->restView()->setData($this->restResponseModel)->setStatusCode($this->restResponseModel->getCode());
    }

    /**
     * @param Request $request
     *
     * @return RequestModel
     */
    private function validateRestRequest(Request $request)
    {
        $requestModel = $this->apiM->modelRest()->request()->userWs()->version2()->userApplicationRole()->show();

        $requestModel->setIdentifier($request->query->get('identifier'));
        $requestModel->setApplicationName($request->query->get('applicationName'));

        $validationErrors = $this->apiM->validator()->validate($requestModel);

        if (count($validationErrors) > 0) {
            foreach ($validationErrors as $error) {
                $message = $error->getPropertyPath() . ": " . $error->getMessage();
                $this->errors->addError(400, $message, 'external')->setCode(400);
            }
        }

        return $requestModel;
    }

    /**
     * @param string $identifier
     * @param $applicationName
     *
     * @return ResponseUserType
     */
    private function sendXmlRequest($identifier, $applicationName)
    {
        $requestModel = $this->apiM->modelXml()->request()->userWs()->version2()->userApplicationRole()
            ->show()->request();
        $userApplicationRoleModelXml = $this->apiM->modelXml()->request()->userWs()->version2()
            ->userApplicationRole()->show()->userApplicationRole($identifier, $applicationName);
        $requestModel->setUserApplicationRole($userApplicationRoleModelXml);

        // add credentials
        $credentialsModelXml = $this->apiM->modelXml()->request()->userWs()->version2()->user()->show()->credentials();
        $credentialsModelXml->setCredentialsData(
            $this->userToken->servicetoken,
            'UserApplicationRoleShow',
            $this->userToken->useridentifier,
            $this->userToken->username,
            'en'
        );
        $requestModel->setCredentials($credentialsModelXml);

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

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

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

    /**
     *
     * @param string $xmlResponse
     * @return ResponseUserType
     */
    private function buildItem($xmlResponse)
    {
        $responseUserType = $this->apiM->modelRest()->response()->userWs()->version2()->userApplicationRole()->show()->userType();

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

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

    /**
     * @param string $xmlString
     * @param ResponseUserType $responseUserType
     *
     * @return ResponseUserType
     */
    private function buildSuccess($xmlString, ResponseUserType $responseUserType)
    {
        $this->successXmlEntity = $this->apiM->serializer()->deserialize($xmlString, SuccessXMLResponse::class, 'xml');

        if ($this->successXmlEntity->getStatus() !== 'OK') {
            return $this->buildError($xmlString, $responseUserType);
        }

        $responseUserType = $this->buildSuccessUserType($responseUserType);

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

        return $responseUserType;
    }

    /**
     * @param string $xmlString
     * @param ResponseUserType $responseUserType
     *
     * @return ResponseUserType
     */
    private function buildError($xmlString, ResponseUserType $responseUserType)
    {
        $this->errorXmlEntity = $this->apiM->serializer()->deserialize($xmlString, ErrorXMLResponse::class, 'xml');

        foreach ($this->errorXmlEntity->getErrors() as $error) {
            $errorItem = $this->apiM->modelRest()->response()->userWs()->version2()->userApplicationRole()->show()->error();
            $message = 'ErrorCode: ' . $error->getErrorCode() . ' ErrorRef: ' . $error->getErrorRef() . ' ShortMessage: ' . $error->getShortMessage();
            $errorItem->setMessage($message);
            $errorItem->setCode(400);
            $errorItem->setException(static::ERROR_EXTERNAL);
            $responseUserType->setError($errorItem);
        }

        $responseUserType->setCode($this->errorXmlEntity->getCode());
        $responseUserType->setStatus($this->errorXmlEntity->getStatus());

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

        return $responseUserType;
    }

    /**
     * @param ResponseShow\UserType $restResponseUserType
     *
     * @return mixed
     */
    private function buildSuccessUserType(ResponseShow\UserType $restResponseUserType)
    {
        $xmlResponseUserApplicationRoleType = $this->successXmlEntity->getUserApplicationRole()->getShow();

        $this->apiM->helper()->mapper()->modelInToModelOut(
            $xmlResponseUserApplicationRoleType->getUserType(),
            $restResponseUserType
        );

        $restResponseUserType->setCode($this->successXmlEntity->getCode());
        $restResponseUserType->setStatus($xmlResponseUserApplicationRoleType->getStatus());

        if ($xmlResponseUserApplicationRoleType->getApplicationType() !== null) {
            /** @var ResponseShow\ApplicationType $responseApplicationType */
            $responseApplicationType = $this->apiM->helper()->mapper()->modelInToModelOut(
                $xmlResponseUserApplicationRoleType->getApplicationType(),
                new ResponseShow\ApplicationType()
            );

            foreach ($xmlResponseUserApplicationRoleType->getApplicationType()->getRoleType() as $xmlResponseRoleType) {
                /** @var ResponseShow\RoleType $responseRoleType */
                $responseRoleType = $this->apiM->helper()->mapper()->modelInToModelOut(
                    $xmlResponseRoleType,
                    new ResponseShow\RoleType()
                );
                $responseApplicationType->setRole($responseRoleType);
            }
            $restResponseUserType->setApplicationType($responseApplicationType);
        }

        return $restResponseUserType;
    }
}
