<?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\WebserviceBundle\Api\Controller;

use Sso\WebserviceBundle\Api\Error\Type\External as ExternalError;
use Sso\WebserviceBundle\Api\Response\Builder as XmlResponse;
use Sso\WebserviceBundle\Entity\Webservice\Type\User;

/**
 * Class UserController
 * @package Sso\WebserviceBundle\Api\Controller
 */
class UserController extends AbstractController
{
    /**
     * @return array
     */
    protected function _getActions()
    {
        return array(
            'Add',
            'Update',
            'ChangePassword',
            'Delete',
            'Show',
        );
    }

    /**
     * Show user
     *
     * @param \SimpleXMLElement $xmlRequest
     * @param XmlResponse $xmlResponse
     */
    protected function show(\SimpleXMLElement $xmlRequest, XmlResponse $xmlResponse)
    {

        $requestModel = $this->modelFactory->request()->user()->show($xmlRequest);
        $userModel = $requestModel->getUserModel();

        if (!$requestModel->hasErrors() && !($user = $this->dbM->user()->getUserByUser($userModel))) {
            $requestModel->addError(new ExternalError('u011', 'UserNotFound'));
        }
        if (!$requestModel->hasErrors() && !$this->apiM->userAuthorization()->canShow($user)) {
            $requestModel->addError(new ExternalError('u012', 'UserNotAuthorized'));
        }
        if (!$requestModel->hasErrors()) {
            $requestModel->setUserModel($user);
        }
        $this->modelFactory->response($xmlResponse)->user()->show($requestModel);
    }

    /**
     * Add user
     *
     * @param \SimpleXMLElement $xmlRequest
     * @param XmlResponse $xmlResponse
     */
    protected function add(\SimpleXMLElement $xmlRequest, XmlResponse $xmlResponse)
    {
        $requestModel = $this->modelFactory->request()->user()->add($xmlRequest);
        $userModel = $requestModel->getUserModel();

        if (!$requestModel->hasErrors()) {
            $userModel->generateGuid();
            if (!$userModel->getActive()) {
                $userModel->generateActivateRequestToken();
            }
            $this->updateMfaSecret($userModel);
            $this->dbM->user()->saveUser($userModel);
        }

        if (!$requestModel->hasErrors()) {
            $this->apiM->container->get('sso_access.authorization.temporary_token_authorization_interface')
                ->addAuthorizedUserId($userModel->getId());
        }

        $this->modelFactory->response($xmlResponse)->user()->add($requestModel);
    }

    /**
     * Update user
     *
     * @param \SimpleXMLElement $xmlRequest
     * @param XmlResponse $xmlResponse
     */
    protected function update(\SimpleXMLElement $xmlRequest, XmlResponse $xmlResponse)
    {
        $requestModel = $this->modelFactory->request()->user()->update($xmlRequest);
        $userModel = $requestModel->getUserModel();
        $userUpdateModel = $requestModel->getUserUpdateModel();

        if (!$requestModel->hasErrors() && !($user = $this->dbM->user()->getUserByUser($userUpdateModel))) {
            $requestModel->addError(new ExternalError('u021', 'UserNotFound'));
        }
        if (!$requestModel->hasErrors() && !$this->apiM->userAuthorization()->canUpdate($user)) {
            $requestModel->addError(new ExternalError('u022', 'UserNotAuthorized'));
        }

        if (!$requestModel->hasErrors()) {
            $user->merge($userModel);
            $user->setValidator($this->apiM->validator());
            $this->updateMfaSecret($user);
            $this->updateActivateToken($user);
            if (!$this->dbM->user()->saveUser($user)) {
                $requestModel->addErrors($user->errors()->getErrors());
            }
        }
        $this->modelFactory->response($xmlResponse)->user()->update($requestModel);
    }

    /**
     * Change Password
     *
     * @param \SimpleXMLElement $xmlRequest
     * @param XmlResponse $xmlResponse
     */
    protected function changePassword(\SimpleXMLElement $xmlRequest, XmlResponse $xmlResponse)
    {
        $requestModel = $this->modelFactory->request()->user()->changePassword($xmlRequest);
        $userModel = $requestModel->getUserModel();
        $userUpdateModel = $requestModel->getUserUpdateModel();

        if (!$requestModel->hasErrors() && !($user = $this->dbM->user()->getUserByUser($userUpdateModel))) {
            $requestModel->addError(new ExternalError('u031', 'UserNotFound'));
        }
        if (!$requestModel->hasErrors() && !$this->apiM->userAuthorization()->canUpdate($user)) {
            $requestModel->addError(new ExternalError('u032', 'UserNotAuthorized'));
        }
        if (!$requestModel->hasErrors()) {
            $user->merge($userModel);
            $user->setValidator($this->apiM->validator());
            if (!$this->dbM->user()->saveUser($user)) {
                $requestModel->addErrors($user->errors()->getErrors());
            }
        }
        $this->modelFactory->response($xmlResponse)->user()->changePassword($requestModel);
    }

    /**
     * Delete user
     *
     * @param \SimpleXMLElement $xmlRequest
     * @param XmlResponse $xmlResponse
     */
    protected function delete(\SimpleXMLElement $xmlRequest, XmlResponse $xmlResponse)
    {
        $requestModel = $this->modelFactory->request()->user()->delete($xmlRequest);
        $userModel = $requestModel->getUserModel();

        $user = null;
        if (!$requestModel->hasErrors() && !($user = $this->dbM->user()->getUserByUser($userModel))) {
            $requestModel->addError(new ExternalError('u041', 'UserNotFound'));
        }
        if (!$requestModel->hasErrors() && !$this->apiM->userAuthorization()->canDelete($user)) {
            $requestModel->addError(new ExternalError('u042', 'UserNotAuthorized'));
        }
        if (!$requestModel->hasErrors() && !$this->dbM->user()->deleteUser($user)) {
            $requestModel->addErrors($user->errors()->getErrors());
        }
        if (null !== $user) {
            $requestModel->getUserModel()->setUsername($user->getUsername());
        }
        $this->modelFactory->response($xmlResponse)->user()->delete($requestModel);
    }

    /**
     * Update MFA secret if necessary
     *
     * @param User $user
     * @throws \Exception
     */
    private function updateMfaSecret(User $user)
    {
        if ($user->isMfaEnabled() && (!$user->getMfaSecret() || $user->isMfaRecreate())) {
            $user->setMfaSecret($this->apiM->container->get('mfa_api_manager')->mfa()->create()->generateSecret());
        }
    }

    /**
     * Update ActivateToken if necessary
     *
     * @param User $user
     */
    private function updateActivateToken(User $user)
    {
        if (
            !$user->isActive() &&                    // Only inactive users are allowed to generate a new token
            0 < strlen($user->getActivateToken()) && // Manually disabled users do not have an activate-token. Do not allow them to activate their account!
            $user->hasActivateTokenExpired() &&      // No need to send a token if current token has not expired
            $user->isActivateTokenRecreate()         // Recreate token if flag is set
        ) {
            $user->generateActivateRequestToken();
        }
    }
}
