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

namespace Sso\BackendBundle\Controller;

use Sso\BackendBundle\Form;
use Sso\BackendBundle\Model\Form\UserCsvConfiguration;
use Sso\BackendBundle\Model\Form\UserCsvRow;
use Sso\WebserviceBundle\Entity\Webservice\Type;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Class WsUserController
 *
 * @copyright  2016 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package Sso\BackendBundle\Controller
 */
class WsUserController extends WsAbstractController
{

    /**
     * @return Response
     */
    public function indexAction()
    {
        // Prepare pager
        $controller = $this;
        $routeGenerator = function ($page) use ($controller) {
            return $controller->generateUrl('_admin_backend_webservice_user', array('page' => $page));
        };
        list($filterForm, $queryBuilder) = $this->filter(
            new Form\WsUserFilterType(),
            $this->userHandler()->getUserRepository()
        );
        list($entities, $pagerHtml) = $this->paginator($queryBuilder, $routeGenerator);

        if ($this->container->hasParameter('sso_rest')) {
            $restParams = $this->container->getParameter('sso_rest');
            if (isset($restParams['blocking_enabled'])) {
                $userBlockingEnabled = $restParams['blocking_enabled'];
                $maxLoginFails = $restParams['max_login_fails'];
                $clearTimeSec = $restParams['clear_time_sec'];
            }
        }
        return $this->render('SsoBackendBundle:WsUser:index.html.twig', array(
            'entities' => $entities,
            'pagerHtml' => $pagerHtml,
            'filterForm' => $filterForm->createView(),
            'userBlockingEnabled' => isset($userBlockingEnabled) ? $userBlockingEnabled : false,
            'maxLoginFails' => isset($maxLoginFails) ? $maxLoginFails : false,
            'clearTimeSec' => isset($clearTimeSec) ? $clearTimeSec : false,
        ));
    }

    /**
     * Ajax search for user
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @return JsonResponse
     */
    public function searchAction(Request $request)
    {
        if (!$request->isXmlHttpRequest()) {
            return new Response('This is no ajax request!', 400);
        }

        $query = $request->get('query', '');
        $limit = $request->get('limit', 10);
        $offset = ($request->get('page', 1) - 1) * $limit;
        $items = array();
        foreach ($this->userHandler()->getUsersByUsername($query, $limit, $offset) as $user) {
            array_push($items, array(
                'id' => $user->getId(),
                'text' => $user->getUsername(),
            ));
        }

        $response = new JsonResponse(array(
            'total' => count($items),
            'items' => $items,
        ));
        if (null !== ($callback = $request->get('callback'))) {
            $response->setCallback($callback);
        }

        return $response;
    }

    /**
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @return Response
     */
    public function createAction(Request $request)
    {
        $userModel = new \Sso\WebserviceBundle\Entity\Webservice\Type\User($this->container->get('validator'));
        $userModel->generateId();
        $userModel->generateGuid();
        $form = $this->createCreateForm($userModel);
        $form->handleRequest($request);

        if ($form->isValid()) {
            $this->updateMfaSecret($userModel);
            $this->userHandler()->saveUser($userModel);
            $this->get('session')->getFlashBag()->add('success', 'User created success');

            return $this->redirect(
                $this->generateUrl('_admin_backend_webservice_user_show', ['userId' => $userModel->getId()])
            );
        }

        return $this->render(
            'SsoBackendBundle:WsUser:new.html.twig',
            ['entity' => $userModel, 'form' => $form->createView()]
        );
    }

    /**
     * @return Response
     */
    public function newAction()
    {
        $entity = new Type\User($this->container->get('validator'));
        $form = $this->createCreateForm($entity);

        return $this->render(
            'SsoBackendBundle:WsUser:new.html.twig',
            [
                'entity' => $entity,
                'form' => $form->createView(),
            ]
        );
    }

    /**
     * @param string $userId
     * @param int $showApplications
     * @return Response
     */
    public function showAction($userId, $showApplications)
    {
        $entityUser = $this->userHandler()->getUserRepository()->find($userId);
        if (!$entityUser) {
            throw $this->createNotFoundException('Unable to find WebserviceUser entity.');
        }

        $qrMessage = '';
        if ($entityUser->isMfaEnabled()) {
            $qrMessage = $this->container->get('mfa_api_manager')->mfa()->otpAuth()->getUri($entityUser->getEmail(),
                $entityUser->getMfaSecret());
        }

        $deleteForm = $this->createDeleteForm($userId);

        return $this->render('SsoBackendBundle:WsUser:show.html.twig', array(
            'entity' => $entityUser,
            'delete_form' => $deleteForm->createView(),
            'showApplications' => $showApplications,
            'qrMessage' => $qrMessage,
        ));
    }

    /**
     *
     * @param integer $userId
     * @return Response
     * @throws NotFoundHttpException
     */
    public function editAction($userId)
    {
        $entity = $this->userHandler()->getUserById($userId);
        if (!$entity) {
            throw $this->createNotFoundException('User not found!');
        }

        $editForm = $this->createEditForm($entity);
        $deleteForm = $this->createDeleteForm($userId);

        return $this->render(
            'SsoBackendBundle:WsUser:edit.html.twig',
            array(
                'entity' => $entity,
                'edit_form' => $editForm->createView(),
                'delete_form' => $deleteForm->createView(),
            )
        );
    }

    /**
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @param string $userId
     * @return Response
     */
    public function updateAction(Request $request, $userId)
    {
        $entity = $this->userHandler()->getUserById($userId);
        if (!$entity) {
            throw $this->createNotFoundException('User not found!');
        }
        $deleteForm = $this->createDeleteForm($userId);
        $editForm = $this->createEditForm($entity);
        $editForm->handleRequest($request);

        if ($editForm->isValid()) {
            $this->updateMfaSecret($entity);
            $this->updateActivateToken($entity);
            $this->userHandler()->saveUser($entity);
            $this->get('session')->getFlashBag()->add('info', 'User Saved Sucess');

            return $this->redirect(
                $this->generateUrl(
                    '_admin_backend_webservice_user_edit',
                    array('userId' => $userId)
                )
            );
        }

        return $this->render(
            'SsoBackendBundle:WsUser:edit.html.twig',
            array(
                'entity' => $entity,
                'edit_form' => $editForm->createView(),
                'delete_form' => $deleteForm->createView(),
            )
        );
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function csvConfigureAction(Request $request): Response
    {
        $csvRowConfiguration = null;
        if ($request->query->has('config_id')) {
            $csvRowConfiguration = @unserialize($this->get('session')->get('UserCsvImport' . $request->query->get('config_id')));
        }
        if (!$csvRowConfiguration instanceof UserCsvConfiguration) {
            $csvRowConfiguration = new UserCsvConfiguration();
            $csvRowConfiguration->setRows([new UserCsvRow()]);
        }

        $formOptions = ['password_encrypt_type_choices' => $this->get('password_crypt_repository')->getEncryptionTypes()];
        if ($request->isXmlHttpRequest()) {
            // Do not validate ajax requests
            $formOptions['validation_groups'] = false;
        }
        $form = $this->createForm(Form\WsUserCsvConfigurationType::class, $csvRowConfiguration, $formOptions);
        $form->handleRequest($request);

        if (!$request->isXmlHttpRequest() && $form->isSubmitted() && $form->isValid()) {
            $this->get('session')->set('UserCsvImport' . $csvRowConfiguration->getId(), serialize($csvRowConfiguration));
            return $this->redirectToRoute('_admin_backend_webservice_user_import', ['config_id' => $csvRowConfiguration->getId()]);
        }

        return $this->render(
            '@SsoBackend/WsUser/csv-configure.html.twig',
            [
                'form' => $form->createView(),
                'maxUploadSize' => ini_get('upload_max_filesize'),
            ]
        );

    }

    /**
     * @param Request $request
     * @return Response
     */
    public function csvUpdateAction(Request $request): Response
    {
        $configuration = @unserialize($this->get('session')->get('UserCsvImport' . $request->query->get('config_id')));
        if (!$configuration instanceof UserCsvConfiguration) {
            return $this->redirectToRoute('_admin_backend_webservice_user_configure');
        }

        $form = $this->createForm(Form\WsUserCsvImportType::class);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            $formData = $form->getData();
            $resultFile = $this->get('sso_backend.worker.user_csv_update')->import($formData['csvFile'], $configuration);

            // Send result as download file
            $response = new BinaryFileResponse($resultFile);
            $mimeTypeGuesser = new FileinfoMimeTypeGuesser();
            if ($mimeTypeGuesser->isSupported()) {
                $response->headers->set('Content-Type', $mimeTypeGuesser->guess($resultFile));
            } else {
                $response->headers->set('Content-Type', 'text/plain');
            }

            $response->setContentDisposition(
                ResponseHeaderBag::DISPOSITION_ATTACHMENT,
                basename($resultFile)
            );

            return $response;
        }

        return $this->render('@SsoBackend/WsUser/csv-import.html.twig', [
            'form' => $form->createView(),
            'importConfiguration' => $configuration,
            'maxUploadSize' => ini_get('upload_max_filesize'),
        ]);
    }

    /**
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @param string $userId
     * @return Response
     */
    public function deleteAction(Request $request, $userId)
    {
        $form = $this->createDeleteForm($userId);
        $form->handleRequest($request);

        $entity = $this->userHandler()->getUserById($userId);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find User entity.');
        }

        $this->userHandler()->deleteUser($entity);
        $this->get('session')->getFlashBag()->add('success', 'User deleted.');

        return $this->redirect($this->generateUrl('_admin_backend_webservice_user'));
    }

    /**
     * Creates a form to create a UserType entity.
     * @param Type\User $entity The entity
     * @return \Symfony\Component\Form\Form The form
     */
    private function createCreateForm(Type\User $entity)
    {
        $form = $this->createForm(
            new Form\WsUserType(),
            $entity,
            [
                'action' => $this->generateUrl('_admin_backend_webservice_user_create'),
                'method' => 'POST',
                'password_policy_choices' => $this->databaseManager()->webservice()->passwordPolicy()->getChoiceList(),
                'password_encrypt_type_choices' => $this->get('password_crypt_repository')->getEncryptionTypes(),
            ]
        );

        $form->add('submit', 'submit', array('label' => 'Create'));

        return $form;
    }

    /**
     * Creates a form to edit a User entity.
     *
     * @param Type\User $entity The entity
     * @return \Symfony\Component\Form\Form The form
     */
    private function createEditForm(Type\User $entity)
    {
        $form = $this->createForm(
            new Form\WsUserType(),
            $entity,
            array(
                'action' => $this->generateUrl('_admin_backend_webservice_user_update',
                    array(
                        'userId' => $entity->getId(),
                    )
                ),
                'password_policy_choices' => $this->databaseManager()->webservice()->passwordPolicy()->getChoiceList(),
                'password_encrypt_type_choices' => $this->get('password_crypt_repository')->getEncryptionTypes(),
                'method' => 'PUT',
            )
        );

        $form->add('submit', 'submit', array('label' => 'Update'));

        return $form;
    }

    /**
     * Creates a form to delete a User entity by id.
     *
     * @param mixed $userId The entity id
     *
     * @return \Symfony\Component\Form\Form
     */
    private function createDeleteForm($userId)
    {
        return $this->createFormBuilder()
            ->setAction($this->generateUrl('_admin_backend_webservice_user_delete', array('userId' => $userId)))
            ->setMethod('DELETE')
            ->add('submit', 'submit', array('label' => 'Delete'))
            ->getForm();
    }

    /**
     * @return \Sso\WebserviceBundle\Database\Webservice\User Database handler for user model
     */
    private function userHandler()
    {
        return $this->databaseManager()->webservice()->user();
    }

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

    /**
     * Update ActivateToken if necessary
     *
     * @param Type\User $user
     */
    private function updateActivateToken(Type\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();
        }
    }
}
