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

use Doctrine\Common\Collections\ArrayCollection;
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\UserWs\Version1\UserApplicationAttribute\Show\Item as ScopeAttributesModel;
use Sso\RestBundle\ModelRest\Response\ObjectWs\Version1\Object\Show\Multi\Success\Response as ObjectResponseSuccessModelMulti;
use Sso\RestBundle\ModelRest\Response\ObjectWs\Version1\Object\Show\Multi\Error\Response as ObjectResponseErrorModelMulti;
use Sso\RestBundle\ModelRest\Request\MultipleWs\Version1\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\MultipleWs\Version1\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
     * @return RestView
     */
    public function getUserApplicationAttributes(Request $request)
    {
        $scopeApplicationNameConfig = $this->apiM->configuration()->scopeConfig()->getScopeApplicationName();

        $request->query->add(['applicationName' => $scopeApplicationNameConfig]);
        return $this->apiM->worker()->userWs()->version1()->userApplicationAttribute()->show()->init($request);
    }

    /**
     * @param Request $request
     * @param ScopeAttributesModel $userApplicationAttrResponse
     * @return RestView[]
     */
    public function getObjectsByAttributes(Request $request, ScopeAttributesModel $userApplicationAttrResponse)
    {
        $attributesConfig = $this->apiM->configuration()->scopeConfig()->getScopeAttributes();
        $responseViews = [];

        /** @var ScopeAttributesModel $scopeAttributesModel */
        $scopeAttributesModel = $userApplicationAttrResponse;
        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() == static::SCOPE_ATTRIBUTE_TYPE_MANY) {
                    $responseViews[] = $this->apiM->worker()->objectWs()->version1()->object()->getObjectMulti()->init($request);
                } else {
                    $responseViews[] = $this->apiM->worker()->objectWs()->version1()->object()->getObjectSingle()->init($request);
                }

            }
        }

        return $responseViews;
    }

    /**
     * @param ObjectResponseSuccessModelMulti[] $objectResponses
     * @return \Sso\RestBundle\ModelRest\Response\ObjectWs\Version1\Object\Show\Multi\Success\Response
     * # RestView
     */
    public function buildResponse(array $objectResponses)
    {
        $objectModel = $this->apiM->modelRest()->response()->objectWs()->version1()->object()->show()->multi()->success()->response($this->apiM->helper()->mapper());
        foreach ($objectResponses as $objectResponse) {
            $this->buildSuccessModel($objectResponse, $objectModel);
        }
        $objectModel->setCode(200);
        $objectModel->setDate(new \DateTime());

        return $objectModel;
    }

    /**
     * @param ObjectResponseSuccessModelMulti $jsonObjectResponse
     * @param ObjectResponseSuccessModelMulti $objectModel
     * @return ObjectResponseSuccessModelMulti
     */
    private function buildSuccessModel($jsonObjectResponse, ObjectResponseSuccessModelMulti $objectModel)
    {
        /** @var ObjectResponseSuccessModelMulti $responseObjectModel */
        $responseObjectModel = $jsonObjectResponse;

        $appendObjects = $objectModel->getObjects();

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

        /** @var ObjectResponseSuccessModelMulti $objectModel */
        $objectTypesIn = $objectModel->getObjects();
        $objectTypesOut = new ArrayCollection();
        foreach ($objectTypesIn as $objectType) {
            $modelOut = $this->apiM->modelRest()->response()->objectWs()->version1()->object()->show()->multi()->success()->objectType($this->apiM->helper()->mapper(),
                '');
            $objectTypesOut[] = $this->apiM->helper()->mapper()->modelInToModelOut($objectType, $modelOut);
        }
        $objectModel->setObjects($objectTypesOut);

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

    /**
     * @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,
            ObjectResponseErrorModelMulti::class, $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()->multipleWs()->version1()->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')->setCode(400);
            }
        }

        return $restRequestModel;
    }

}
