<?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.
 *
 * @author      shgb
 * @copyright   2018 Lifestyle Webconsulting GmbH
 * @link        http://www.life-style.de
 */

namespace Sso\RestBundle\Worker\Idp\PostLogin;

use FOS\RestBundle\View\View as RestView;
use FOS\RestBundle\Request\ParamFetcher;
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 FOS\UserBundle\Model\User as BaseUser;
use Sso\RestBundle\Model\Response\Idp\PostLogin\Response as SuccessResponse;
use LifeStyle\Tools\RestErrorBundle\Api\Error\Errors\Entity\Errors as ErrorResponse;

/**
 * Class Handler
 * @package Sso\RestBundle\Worker\Idp\PostLogin
 */
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\Idp\PostLogin\Request
     */
    protected $requestModel;

    /**
     * @var \Sso\RestBundle\Model\Response\Idp\PostLogin\Response
     */
    protected $responseModel;

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

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

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

    /**
     * Handler constructor.
     * @param RestApiManager $restApiM
     */
    public function __construct(RestApiManager $restApiM)
    {
        $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->errorApi = $this->restApiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
    }

    /**
     * @param string $encyptedBody
     * @param $lastDigest
     * @param string $format
     * @return RestView
     * @throws \Exception
     */
    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());
        }

        //now check the login data
        $credentialsValidation = $this->restApiM->validate()->credentials()->loginUser(
            $this->requestModel->getUserIdentifier(),
            $this->requestModel->getPassword()
        );

        //now check for force password reset new policies
        if($credentialsValidation->getStatus() == 200 || $credentialsValidation->getStatus() == 406){
            $pwdResetUserService = $this->restApiM->getContainer()->get('sso_webservice.api_password_policy_user.service');
            $responseCheck = $pwdResetUserService->checkPasswordRenewRequired($credentialsValidation->getUserGuid());
            $credentialsValidation->setForcePasswordReset($responseCheck->isForcePasswordReset());
            if($responseCheck->isForcePasswordReset()){
                $credentialsValidation->setResetPasswordToken($responseCheck->getResetPasswordToken());
                $credentialsValidation->setResetPasswordTokenExpire($responseCheck->getResetPasswordTokenExpire());
            }
        }

        $view = RestView::create();

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

        return $view;
    }

    /**
     * @param $encyptedBody
     * @param $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\Idp\PostLogin\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;
    }
}
