<?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\ObjectWs\Version1\Object\Show\Tree;

use FOS\RestBundle\View\View as RestView;
use Sso\RestBundle\Api\Manager as ApiManager;
use LifeStyle\Tools\RestErrorBundle\Api\Manager as ErrorApi;
use LifeStyle\Tools\RestErrorBundle\Api\Error\Errors\Index as Errors;
use Sso\RestBundle\ModelRest\Request\ObjectWs\Version1\Object\Show\Tree\Request as RequestModel;
use LifeStyle\Tools\RestAuthBundle\Security\Authentication\Token\UidentifyToken;
use Symfony\Component\HttpFoundation\Request;
use GuzzleHttp\Exception\RequestException;
use Sso\RestBundle\ModelXml\Response\ObjectWs\Version1\Object\Show\Tree\Error\Response as ErrorResponse;
use Sso\RestBundle\ModelXml\Response\ObjectWs\Version1\Object\Show\Tree\Success\Response as XmlSuccessResponse;

/**
 * Class Handler
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package    Sso\RestBundle\Worker\ObjectWs\Version1\Object\Show\Tree
 */
class Handler
{
    /**
     * @var UidentifyToken
     */
    private $userToken;

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

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

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

    /**
     * @var \Sso\RestBundle\ModelXml\Response\ObjectWs\Version1\Object\Show\Tree\Error\Response
     */
    private $errorXmlObject;

    /**
     * @var XmlSuccessResponse
     */
    private $successXmlObject;

    /**
     * @var string
     */
    private $format;

    /**
     * @param ApiManager $apiM
     */
    public function __construct(ApiManager $apiM)
    {
        $this->apiM = $apiM;
        $this->errorApi = $this->apiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->format = 'json';
        $this->userToken = $this->apiM->getToken();
    }

    /**
     *
     * @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 object ws ObjectGet
        $resModelXmlArray = $this->send($requestModelRest);

        // third: build response
        if ($resModelXmlArray['status']) {
            //ok
            $view = $this->buildSuccessView($resModelXmlArray['xml']);
        } else {
            //error
            $view = $this->buildErrorView($resModelXmlArray['xml']);
        }

        return $view;
    }

    /**
     * @param Request $request
     * @return RequestModel
     */
    private function validateRestRequest(Request $request)
    {
        $this->format = preg_match('/\.xml/i', $request->getUri()) ? 'xml' : 'json';

        $restRequestModel = $this->apiM->modelRest()->request()->objectWs()->version1()->object()->show()->tree();

        $restRequestModel
            ->setGuid($request->query->get('guid'))
            ->setReferenceId($request->query->get('referenceId'))
            ->setOffset($request->query->get('offset'))
            ->setLimit($request->query->get('limit'));

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

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

        return $restRequestModel;
    }

    /**
     *
     * @param string $xmlString
     * @return RestView
     */
    private function buildSuccessView($xmlString)
    {
        $this->successXmlObject = $this->apiM->serializer()->deserialize($xmlString, XmlSuccessResponse::class, 'xml');
        $modelRestResponse = $this->apiM->modelRest()->response()->objectWs()->version1()->object()->show()->tree()->success()->response();

        $modelRestResponse = $this->apiM->helper()->mapper()->modelInToModelOut($this->successXmlObject,
            $modelRestResponse);
        $modelRestResponse->setCode(200);
        $modelRestResponse->setScriptTimeSec($this->apiM->scriptTimeSeconds());
        $modelRestResponse->setCount($this->successXmlObject->getObject()->getShow()->getCount());
        $modelRestResponse->setOffset($this->successXmlObject->getObject()->getShow()->getOffset());
        $modelRestResponse->setLimit($this->successXmlObject->getObject()->getShow()->getLimit());

        // map and set object type models
        $xmlObjectTypes = $this->successXmlObject->getObject()->getShow()->getObjectType();
        foreach ($xmlObjectTypes as $objectType) {
            $mappedModel = $this->apiM->helper()->mapper()->modelInToModelOut($objectType,
                $this->apiM->modelRest()->response()->objectWs()->version1()->object()->show()->tree()->success()->objectType());
            $modelRestResponse->addObject($mappedModel);
        }

        $view = RestView::create();
        $view->setData($modelRestResponse);
        $view->setStatusCode(200);

        return $view;
    }

    /**
     *
     * @param string $xmlString
     * @return RestView
     */
    private function buildErrorView($xmlString)
    {
        $responseItem = $this->apiM->modelRest()->response()->objectWs()->version1()->object()->show()->tree()->error()->response();
        $this->errorXmlObject = $this->apiM->serializer()->deserialize($xmlString, ErrorResponse::class, 'xml');

        $responseItem->setTrackId($this->errorXmlObject->getTrackid());
        $responseItem->setScriptTimeSec($this->apiM->scriptTimeSeconds());
        $responseItem->setCode(400);

        $errors = $this->errorXmlObject->getObject()->getTreeView()->getError();

        foreach ($errors as $error) {
            $errorItem = $this->apiM->modelRest()->response()->objectWs()->version1()->object()->show()->tree()->error()->error();
            $message = 'ErrorCode: ' . $error->getErrorCode() . ' ErrorRef: ' . $error->getErrorRef() . ' ShortMessage: ' . $error->getShortMessage();
            $errorItem->setMessage($message);
            $errorItem->setCode(400);
            $errorItem->setException('external');
            $responseItem->addError($errorItem);
        }

        $view = RestView::create();
        $view->setData($responseItem);
        $view->setStatusCode(400);

        return $view;
    }

    /**
     * @param RequestModel $request
     * @return array
     */
    public function send(RequestModel $request)
    {
        $requestModelXml = $this->apiM->modelXml()->request()->objectWs()->version1()->object()->show()->tree()->request();

        // add credentials
        $credentialsModelXml = $this->apiM->modelXml()->request()->objectWs()->version1()->object()->show()->tree()->credentials();
        $credentialsModelXml->setCredentialsData($this->userToken->servicetoken, 'MyServiceName',
            $this->userToken->useridentifier, $this->userToken->username, 'en');
        $requestModelXml->setCredentials($credentialsModelXml);

        // add objecttype
        $objectModelXml = $this->apiM->modelXml()->request()->objectWs()->version1()->object()->show()->tree()->object();
        $objectModelXml->setObjectTypeData($request->getGuid(), $request->getReferenceId(), $request->getOffset(),
            $request->getLimit());
        $requestModelXml->setObject($objectModelXml);

        $options = array(
            'headers' => array(
                'API-VERSION' => '1',
                'API-CONTROLLER' => 'Object',
                'API-ACTION' => 'TreeView',
                'Content-Type' => 'text/xml; charset=UTF-8',
                'Accept' => 'text/xml,application/xml;q=0.9,*/*;q=0.8',
            ),
            'body' => $this->apiM->serializer()->serialize($requestModelXml, 'xml'),
        );

        $serviceUrl = $this->apiM->configuration()->objectWs()->getBaseUrl();
        $serviceUrl .= $this->apiM->configuration()->objectWs()->getServiceAction();

        try {
            $clientResponse = $this->apiM->client()->post($serviceUrl, $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()];
    }
}
