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

namespace Sso\RestBundle\Worker\Idp\PostLoginFailed;

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;
use Sso\RestBundle\Api\Database\Factory as DatabaseManager;
use Sso\WebserviceBundle\Entity\Webservice\Type\User as SsoUser;

class Handler
{
    /**
     * @var RestApiManager
     */
    protected $restApiM;

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

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

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

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

    /**
     * @var \Sso\RestBundle\Model\Request\Idp\PostLoginFailed\Request
     */
    protected $requestModel;

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

    /**
     * @var SsoUser
     */
    private $ssoUser;

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

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

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


    /**
     *
     * @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->dbM = $this->restApiM->database();
        $this->errorApi = $this->restApiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->responseModel = $this->restApiM->model()->response()->idp()->postLoginFailed();

    }


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

        $this->validateUserIdentifier();
        if ($this->errors->hasErrors()) {
            return $this->errors->getErrors();
        }

        //now set login fail
        $this->ssoUser->setLoginFailed();
        $this->dbM->webservice()->data()->user()->save($this->ssoUser);

        // now fill the responseModel
        $this->setResponseModel();

        $view = RestView::create();

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

        return $view;
    }

    /**
     * @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\Idp\PostLoginFailed\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;

    }

    /**
     * @throws \Exception
     */
    private function validateUserIdentifier()
    {

        $this->ssoUser = $this->dbM->webservice()->data()->user()->findUserByIdentifier($this->requestModel->getUserIdentifier());
        if (!$this->ssoUser) {
            $this->addError('user not found in database');
            return;
        }
    }


    private function setResponseModel()
    {
        $this->responseModel->setStatus(200);
        $this->responseModel->setMessage('OK');
        $this->responseModel->setUsername($this->ssoUser->getUsername());
        $this->responseModel->setUserFirstname($this->ssoUser->getFirstname());
        $this->responseModel->setUserLastname($this->ssoUser->getLastname());
        $this->responseModel->setUserIdentifier($this->requestModel->getUserIdentifier());
        $this->responseModel->setUserEmail($this->ssoUser->getEmail());
        $this->responseModel->setMfaEnabled($this->ssoUser->isMfaEnabled());
        $this->responseModel->setLastLogin($this->ssoUser->getLastLoginAt());
        $this->responseModel->setLoginFails($this->ssoUser->getLoginFails());
        $this->responseModel->setLoginFailedFirstAt($this->ssoUser->getLoginFailedFirstAt());
        $this->responseModel->setLoginFailedLastAt($this->ssoUser->getLoginFailedLastAt());
    }

    /**
     * @param string $message
     * @throws \Exception
     */
    private function addError($error, $status = 400)
    {
        // before killing the process set a failed login if the user exists
        $this->errors->addError($status, $error, 'external', 'InvalidRequest')->setStatus($status);
    }
}
