<?php

/**
 * @author sb
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is protected 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  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\RestBundle\Worker\User\ApplicationAttributes;

use FOS\RestBundle\View\View as RestView;
use Sso\RestBundle\Api\Manager as RestApiManager;
use LifeStyle\Tools\RestErrorBundle\Api as ErrorApi;
use LifeStyle\Tools\RestErrorBundle\Api\Error\Errors\Index as Errors;
use Sso\WebserviceBundle\Database\Webservice\Manager as DatabaseManager;
use Sso\WebserviceBundle\Entity\Webservice\Type\User as SsoUser;

/**
 * Class Handler
 * @package Sso\RestBundle\Worker\User\ApplicationAttributes
 */
class Handler
{
    /**
     * @var RestApiManager
     */
    protected $restApiM;

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

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

    /**
     * @var \FOS\UserBundle\Doctrine\UserManager
     */
    protected $userManager;

    /**
     * @var \Sso\RestBundle\Model\Request\User\PostApplicationAttributes\Request
     */
    protected $requestModel;

    /**
     * @var \Sso\RestBundle\Model\Response\User\PostApplicationAttributes\Response
     */
    protected $responseModel;

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

    /**
     * @var integer
     */
    protected $timeStart;

    /**
     * @var integer
     */
    protected $timeEnd;

    /**
     * @var DatabaseManager
     */
    protected $dbM;

    /**
     * @param RestApiManager $restApiM
     * @param DatabaseManager $databaseManager
     */
    public function __construct(RestApiManager $restApiM, DatabaseManager $databaseManager)
    {
        $this->restApiM = $restApiM;

        if ($this->restApiM->session()->has('ls_start_microtime')) {
            $this->timeStart = $this->restApiM->session()->get('ls_start_microtime');
        } else {
            $this->timeStart = microtime(true);
        }

        $this->dbM = $databaseManager;

        $this->errorApi = $this->restApiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->responseModel = $this->restApiM->model()->response()->user()->response()->response();
    }


    /**
     * @param string $encyptedBody
     * @param string $lastDigest
     * @param string $format
     * @return RestView
     */
    public function init($encyptedBody, $lastDigest, $format = 'json')
    {
        $this->format = $format;

        //first things first validate request
        $this->validateRestRequest($encyptedBody, $lastDigest);
        if ($this->errors->hasErrors()) {
            return $this->errorApi->error()->view()->getErrorsView($this->errors->getErrors(), 400);
        }

        //now check if the user exists
        $user = $this->getUser();
        if ($this->errors->hasErrors()) {
            return $this->errorApi->error()->view()->getErrorsView($this->errors->getErrors(), 400);
        }

        //nowbuild Response
        $this->buildResponseModel($user);


        // $credentialsValidation = $this->restApiM->validate()->credentials()->loginUser($this->requestModel->getUserIdentifier(), $this->requestModel->getPassword());

        $view = RestView::create();

        $this->timeEnd = microtime(true);
        $durationSec = $this->timeEnd - $this->timeStart;
        $this->responseModel->setDurationSeconds($durationSec);
        $view->setStatusCode(200)->setData($this->responseModel);

        return $view;
    }

    /**
     * @throws \Exception
     * @return SsoUser
     */
    private function getUser()
    {
        $user = $this->dbM->user()->getUserByUsername($this->requestModel->getUsername());
        if (!$user) {
            $this->errors->addError('cc4', 'external', 'user not found in database');
            $this->errors->setStatus(400);

            return null;
        }

        return $user;
    }

    /**
     * @param string $encyptedBody
     * @param string $lastDigest
     * @return \Sso\RestBundle\Model\Request\Token\PostToken\Request|void
     */
    private function validateRestRequest($encyptedBody, $lastDigest)
    {
        //first we have to decipher the request body
        $requestBody = $this->restApiM->getContainer()->get('ciphering_api_manager')->ciphering()->decoder()->decode($encyptedBody,
            $lastDigest);

        if (!$requestBody) {
            $this->errors->addError("ci1", 'the body could not be decoded', 'external', 'InvalidCipheringException');
            $this->errors->setStatus(400);

            return;
        }

        //no validate the content
        if ($this->format == "xml") {
            $errors = $this->restApiM->helper()->string()->validateXML($requestBody);
        } else {
            $errors = $this->restApiM->helper()->string()->validateJson($requestBody);
        }

        // collect errors
        foreach ($errors as $error) {
            $this->errors->addError(400, $error, 'external', 'InvalidRequest')->setStatus(400);

            return;
        }

        //now build request model
        $this->requestModel = $this->restApiM->serializer()->deserialize(
            $requestBody,
            'Sso\RestBundle\Model\Request\User\PostApplicationAttributes\Request',
            $this->format
        );

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

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

        return $this->requestModel;
    }

    /**
     * @param SsoUser $user
     */
    private function buildResponseModel(SsoUser $user)
    {
        $this->responseModel->setStatus(200);
        $this->responseModel->setMessage('OK');
        $this->responseModel->setUsername($user->getUsername());

        $applications = $this->requestModel->getApplications();
        if (empty($applications)) {
            return;
        }

        foreach ($applications as $applicationType) {
            if ('*' === $applicationType->getName()) {
                foreach ($user->getUserApplications() as $userApplication) {
                    if (!$userApplication->getActive()) {
                        continue;
                    }
                    $responseApplication = $this->restApiM->model()->response()->user()->response()->applicationType($userApplication->getName());
                    $this->responseModel->addApplication($responseApplication);
                }
                continue;
            }
            if (!($application = $this->dbM->application()->getApplicationByName($applicationType->getName()))) {
                continue;
            }
            if (!($userApplication = $this->dbM->userApplication()->getUserApplication($user, $application))) {
                continue;
            }
            if (!$userApplication->getActive()) {
                continue;
            }
            $responseApplication = $this->restApiM->model()->response()->user()->response()->applicationType($application->getName());
            $this->responseModel->addApplication($responseApplication);
            foreach ($userApplication->getAttributes() as $attribute) {
                $responseApplication->addAttribute(
                    $this->restApiM->model()->response()->user()->response()->attribute(
                        $attribute->getName(),
                        $attribute->getValue(),
                        $attribute->getType()
                    )
                );
            }
            foreach ($userApplication->getRoles() as $role) {
                if ($role->getActive()) {
                    $responseApplication->addRole($this->restApiM->model()->response()->user()->response()->role($role->getName()));
                }
            }
        }
    }
}
