<?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\Token\PostToken;

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;

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

    /**
     * @var \Sso\RestBundle\Model\Response\Token\PostToken\Response
     */
    protected $responseModel;

    /**
     *
     * @param RestApiManager $restApiM
     */
    public function __construct(RestApiManager $restApiM)
    {
        $this->restApiM = $restApiM;
        $this->errorApi = $this->restApiM->errorManager();
        $this->errors = $this->errorApi->error()->errors();
        $this->requestModel = $this->restApiM->model()->request()->token()->postToken();
        $this->responseModel = $this->restApiM->model()->response()->token()->postToken();
    }

    /**
     *
     * @param ParamFetcher $paramFetcher
     * @return View
     */
    public function init(ParamFetcher $paramFetcher)
    {
        //first things first validate request
        $this->validateRestRequest($paramFetcher);
        if ($this->errors->hasErrors()) {
            return $this->errorApi->error()->view()->getErrorsView($this->errors->getErrors());
        }

        //next build header data
        $userManager = $this->restApiM->getContainer()->get('fos_user.user_manager');
        $user = $userManager->findUserByUsername($this->requestModel->getUsername());
        $factory = $this->restApiM->getContainer()->get('security.encoder_factory');
        $encoder = $factory->getEncoder($user);
        $password = $encoder->encodePassword($this->requestModel->getPassword(), $this->requestModel->getSalt());
        $header = $this->generateHeader($this->requestModel->getUsername(), $password);

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

        return $view;
    }

    /**
     * @param ParamFetcher $paramFetcher
     * @return \Sso\RestBundle\Model\Request\Token\PostToken\Request
     */
    private function validateRestRequest(ParamFetcher $paramFetcher)
    {
        try {
            $this->requestModel->setUsername($paramFetcher->get('username'));
            $this->requestModel->setPassword($paramFetcher->get('password'));
            $this->requestModel->setSalt($paramFetcher->get('salt'));
        } catch (\InvalidArgumentException $exc) {
            $this->errors->addError($exc->getCode(), $exc->getMessage(), 'external', 'InvalidArgumentException');
            $this->errors->setStatus(400);
        }
        $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');
                $this->errors->setStatus(400);
            }
        }

        $userManager = $this->restApiM->getContainer()->get('fos_user.user_manager');
        $user = $userManager->findUserByUsername($this->requestModel->getUsername());

        if (!$user instanceof BaseUser) {
            $message = "username not found in database";
            $this->errors->addError(400, $message, 'external');
            $this->errors->setStatus(400);
        }

        return $this->requestModel;
    }

    /**
     * Generate token for username given
     *
     * @param  string $username username
     * @param  string $password password with salt included
     * @return string
     */
    private function generateHeader($username, $password)
    {
        $created = date('c');
        $nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
        $nonceSixtyFour = base64_encode($nonce);
        $passwordDigest = base64_encode(sha1($nonce . $created . $password, true));
        $token = sprintf(
            'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"',
            $username,
            $passwordDigest,
            $nonceSixtyFour,
            $created
        );
        $this->responseModel->setUsername($username);
        $this->responseModel->setPasswordDigest($passwordDigest);
        $this->responseModel->setCreated($created);
        $this->responseModel->setNonce($nonceSixtyFour);
        $this->responseModel->setFullHeader($token);
    }
}
