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

use FOS\RestBundle\View\View as RestView;
use Sso\RestBundle\Api\Manager as ApiManager;
use LifeStyle\Tools\RestErrorBundle\Api as ErrorApi;
use LifeStyle\Tools\RestErrorBundle\Api\Error\Errors\Index as Errors;
use GuzzleHttp\Exception\RequestException;
use LifeStyle\Tools\RestAuthBundle\Security\Authentication\Token\UidentifyToken;
use Symfony\Component\HttpFoundation\Request;
use Sso\RestBundle\ModelRest\Request\UserWs\Version2\User\Show\Request as RequestModel;
use Sso\RestBundle\ModelRest\Response\UserWs\Version2\User\Show\UserType as ResponseUserType;
use Sso\RestBundle\ModelXml\Response\UserWs\Version2\User\Show\Success\Response as XMLSuccessResponse;
use Sso\RestBundle\ModelXml\Response\UserWs\Version2\User\Show\Error\Response as XMLErrorResponse;

/**
 * Class Handler
 * @package Sso\RestBundle\Worker\UserWs\Version2\User\Show
 */
class Handler
{
    const STATUS = "status";
    const ERROR_EXTERNAL = "external";

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

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

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

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

    /**
     * @var \Sso\RestBundle\ModelXml\Response\UserWs\Version2\User\Show\Error\Response
     */
    protected $errorXmlEntity;

    /**
     * @var \Sso\RestBundle\ModelXml\Response\UserWs\Version2\User\Show\Success\Response
     */
    protected $successXmlEntity;

    /**
     * @var \Sso\RestBundle\ModelRest\Response\UserWs\Version2\User\Show\Response
     */
    protected $restResponseModel;

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

    /**
     * @param ApiManager $apiM
     */
    public function __construct(ApiManager $apiM)
    {
        $this->apiM = $apiM;
        $this->errorApi = $this->apiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->userToken = $this->apiM->getContainer()->get('security.token_storage')->getToken();
        $this->restResponseModel = $this->apiM->modelRest()->response()->userWs()->version2()->user()->show()->response();
    }

    /**
     * @param Request $request
     * @return RestView
     */
    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
        foreach ($requestModelRest->getIdentifiers() as $identifier) {
            $xmlResponse = $this->sendXmlRequest($identifier);

            $responseItem = $this->apiM->modelRest()->response()->userWs()->version2()->user()->show()->userType();
            if (!$xmlResponse[static::STATUS]) {
                // build error response for a userApplication
                $responseItem = $this->buildError($xmlResponse['xml'], $responseItem);
            } else {
                // fill the success response for a userApplication
                $responseItem = $this->buildSuccess($xmlResponse['xml'], $responseItem);
            }

            $this->restResponseModel->addUserType($responseItem);
        }


        // reset the main code status
        $this->restResponseModel->setCode($this->oneResponseSuccess ? 200 : 400);

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

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

        if (is_array($request->query->get('identifier'))) {
            $requestModel->setIdentifiers($request->query->get('identifier'));
        } else {
            $requestModel->addIdentifier($request->query->get('identifier'));
        }

        $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
     * @return array
     */
    private function sendXmlRequest($identifier)
    {
        $requestModel = $this->apiM->modelXml()->request()->userWs()->version2()->user()->show()->request();
        $userModelXml = $this->apiM->modelXml()->request()->userWs()->version2()->user()->show()->user($identifier);
        $requestModel->setUser($userModelXml);
        // add credentials
        $credentialsModelXml = $this->apiM->modelXml()->request()->userWs()->version2()->user()->show()->credentials();
        $credentialsModelXml->setCredentialsData($this->userToken->servicetoken, 'UserShow',
            $this->userToken->useridentifier, $this->userToken->username, 'en');
        $requestModel->setCredentials($credentialsModelXml);
        $options = array(
            'headers' => array(
                'API-VERSION' => '2',
                'API-CONTROLLER' => 'User',
                'API-ACTION' => 'Show',
                'Content-Type' => 'text/xml; charset=UTF-8',
            ),
            'body' => $this->apiM->serializer()->serialize($requestModel, 'xml'),
        );
        try {
            $clientResponse = $this->apiM->client()->post($this->apiM->configuration()->userWs()->getBaseUrl() . $this->apiM->configuration()->userWs()->getServiceAction(),
                $options);
        } catch (RequestException $ex) {
            $errorResponse = $ex->getResponse()->getBody()->getContents();
            $this->apiM->logger()->error($errorResponse);
            return ['status' => false, 'xml' => $errorResponse];
        }

        return ['status' => true, 'xml' => $clientResponse->getBody()->getContents()];
    }

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

        if ($this->successXmlEntity->getStatus() == 'OK') {

            $responseItem->setGuid($this->successXmlEntity->getUser()->getShow()->getUserType()->getGuid());
            $responseItem->setUsername($this->successXmlEntity->getUser()->getShow()->getUserType()->getUsername());
            $responseItem->setEmail($this->successXmlEntity->getUser()->getShow()->getUserType()->getEmail());
            $responseItem->setFirstname($this->successXmlEntity->getUser()->getShow()->getUserType()->getFirstname());
            $responseItem->setLastname($this->successXmlEntity->getUser()->getShow()->getUserType()->getLastname());
            $responseItem->setActive($this->successXmlEntity->getUser()->getShow()->getUserType()->getActive());

            $responseItem->setDeleted($this->successXmlEntity->getUser()->getShow()->getUserType()->getDeleted());
            $responseItem->setCreatedAt($this->successXmlEntity->getUser()->getShow()->getUserType()->getCreatedAt());
            $responseItem->setLastLoginAt($this->successXmlEntity->getUser()->getShow()->getUserType()->getLastLoginAt());
            $responseItem->setUpdatedAt($this->successXmlEntity->getUser()->getShow()->getUserType()->getUpdatedAt());
            $responseItem->setAuthId($this->successXmlEntity->getUser()->getShow()->getUserType()->getAuthId());
            $responseItem->setLdapSearchAttributes($this->successXmlEntity->getUser()->getShow()->getUserType()->getLdapSearchAttributes());
            $responseItem->setLdapSearchValue($this->successXmlEntity->getUser()->getShow()->getUserType()->getLdapSearchValue());
            $responseItem->setMfaEnabled($this->successXmlEntity->getUser()->getShow()->getUserType()->getMfaEnabled());

        } else {
            return $this->buildError($xmlString, $responseItem);
        }

        $this->oneResponseSuccess = true;

        // Add additional fields to response
        $this->restResponseModel
            ->setCode(200)
            ->setTrackId($this->successXmlEntity->getTrackid())
            ->setDate($this->successXmlEntity->getDate())
            ->setScriptTimeSec($this->apiM->scriptTimeSeconds());

        // Add additional fields to response userApplication
        $responseItem->setCode(200);

        return $responseItem;
    }

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

        foreach ($errors as $error) {
            $errorItem = $this->apiM->modelRest()->response()->userWs()->version2()->user()->show()->error();
            $message = 'ErrorCode: ' . $error->getErrorCode() . ' ErrorRef: ' . $error->getErrorRef() . ' ShortMessage: ' . $error->getShortMessage();
            $errorItem->setMessage($message);
            $errorItem->setCode($this->errorXmlEntity->getCode());
            $errorItem->setException(static::ERROR_EXTERNAL);
            $responseItem->addError($errorItem);
        }

        // Add additional fields to response
        $this->restResponseModel
            ->setCode($this->errorXmlEntity->getCode())
            ->setTrackId($this->errorXmlEntity->getTrackId())
            ->setDate($this->errorXmlEntity->getDate())
            ->setScriptTimeSec($this->apiM->scriptTimeSeconds());

        // Add additional fields to error response
        $responseItem->setCode($this->errorXmlEntity->getCode());

        return $responseItem;
    }
}
