<?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\Scope\GetScopeObjects;

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 Symfony\Component\HttpFoundation\Response;
use Sso\RestBundle\ModelRest\Response\UserApplicationAttribute\Get\Item as ScopeAttributesModel;
use Sso\RestBundle\ModelRest\Response\Object\Get\Multi\Success\Response as ObjectResponseSuccessModelMulti;
use Sso\RestBundle\ModelRest\Response\Object\Get\Multi\Error\Response as ObjectResponseErrorModelMulti;
use Sso\RestBundle\ModelRest\Request\ScopeObjects\Get\Request as RequestModel;
use Symfony\Component\HttpFoundation\Request;

/**
 * Class Handler
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package    Sso\RestBundle\Worker\Scope\GetScopeObjects
 */
class Handler
{
    /**
     * @var string
     */
    const SCOPE_ATTRIBUTE_TYPE_MANY = 'many';

    /**
     * @var string
     */
    const SCOPE_ATTRIBUTE_TYPE_ONE = 'one';

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

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

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

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

    /**
     * @var RequestModel
     */
    protected $requestModelRest;

    /**
     *
     * @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';
    }

    /**
     * @param Request $request
     * @return RestView|null
     */
    public function init(Request $request)
    {
        // first: validate input data
        $this->requestModelRest = $this->validateRestRequest($request);
        if ($this->errors->hasErrors()) {
            return $this->errorApi->error()->view()->getErrorsView($this->errors->getErrors());
        }

        return null;
    }

    /**
     * @param Request $request
     * @param string $scopeApplicationNameConfig
     * @return RestView
     */
    public function getUserApplicationAttributes(Request $request, $scopeApplicationNameConfig)
    {
        $request->query->add(['applicationName' => $scopeApplicationNameConfig]);
        return $this->apiM->worker()->userApplicationAttribute()->getUserApplicationAttribute()->init($request);
    }

    /**
     * @param Request $request
     * @param Response $userApplicationAttrResponse
     * @param array $attributesConfig
     * @return RestView[]
     */
    public function getObjectsByAttributes(
        Request $request,
        Response $userApplicationAttrResponse,
        array $attributesConfig
    ) {
        $responseViews = [];

        /** @var ScopeAttributesModel $scopeAttributesModel */
        $scopeAttributesModel = $this->apiM->serializer()->deserialize($userApplicationAttrResponse->getContent(),
            'Sso\RestBundle\ModelRest\Response\UserApplicationAttribute\Get\Item', $this->format);
        foreach ($scopeAttributesModel->getAttributeTypes() as $attributeType) {

            // if attribute is one of the scope attributes, add it as query param to get objects.
            if (in_array($attributeType->getName(), $attributesConfig)) {

                $request->query->set('referenceId', $attributeType->getValue());

                if ($attributeType->getType() == self::SCOPE_ATTRIBUTE_TYPE_MANY) {
                    $responseViews[] = $this->apiM->worker()->object()->getObjectMulti()->init($request);
                } else {
                    $responseViews[] = $this->apiM->worker()->object()->getObjectSingle()->init($request);
                }

            }
        }

        return $responseViews;
    }

    /**
     * @param Response[] $objectResponses
     * @return RestView
     */
    public function buildResponse(array $objectResponses)
    {
        foreach ($objectResponses as $objectResponse) {
            if (200 !== $objectResponse->getStatusCode()) {
                return $this->buildErrorView($objectResponse->getContent());
            }
        }

        $objectModel = $this->apiM->modelRest()->response()->object()->get()->multi()->success()->response($this->apiM->helper()->mapper());
        foreach ($objectResponses as $objectResponse) {
            $this->buildSuccessModel($objectResponse->getContent(), $objectModel);
        }

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

        return $view;
    }

    /**
     * @param string $jsonObjectResponse
     * @param ObjectResponseSuccessModelMulti $objectModel
     * @return ObjectResponseSuccessModelMulti
     */
    private function buildSuccessModel($jsonObjectResponse, ObjectResponseSuccessModelMulti $objectModel)
    {
        /** @var ObjectResponseSuccessModelMulti $responseObjectModel */
        $responseObjectModel = $this->apiM->serializer()->deserialize($jsonObjectResponse,
            'Sso\RestBundle\ModelRest\Response\Object\Get\Multi\Success\Response', $this->format);

        $appendObjects = $objectModel->getObjects();

        // Map general request data
        $this->apiM->helper()->mapper()->modelInToModelOut($responseObjectModel, $objectModel);

        if (!$appendObjects->isEmpty()) {
            foreach ($appendObjects as $appendObject) {
                $objectModel->addObject($appendObject);
            }
        }

    }

    /**
     * @param string $jsonObjectResponse
     * @return RestView
     */
    private function buildErrorView($jsonObjectResponse)
    {
        $objectErrorModel = $this->apiM->modelRest()->response()->object()->get()->multi()->error()->response();

        /** @var ObjectResponseErrorModelMulti $responseObjectModel */
        $responseObjectModel = $this->apiM->serializer()->deserialize($jsonObjectResponse,
            'Sso\RestBundle\ModelRest\Response\Object\Get\Multi\Error\Response', $this->format);

        /** @var ObjectResponseErrorModelMulti $objectErrorModel */
        $objectErrorModel = $this->apiM->helper()->mapper()->modelInToModelOut($responseObjectModel, $objectErrorModel);

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

        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()->scopeObjects()->get();

        $restRequestModel
            ->setIdentifier($request->query->get('identifier'));

        $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')->setStatus(400);
            }
        }

        return $restRequestModel;
    }
}
