<?php 

namespace NPS\Controller;

use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use NPS\Repository\UserRepository;
use NPS\Factory\TokenFactory;
use NPS\Factory\UserFactory;
use NPS\Model\User;
use NPS\DependencyInjection\Mailer;
use NPS\Repository\TokenRepository;
use NPS\Exception\MissingArgumentException;
use NPS\Exception\InvalidArgumentException;
use NPS\Exception\TokenExpiredException;
use NPS\Exception\ExistingUserException;

/**
 * Controller for user account related stuff
 * @package NPS\Controller
 * @author Lukas Korinek
 */
class UserController extends AbstractController
{

    /**
     * @Route("/user/list")
     * @Security("is_granted('ROLE_MANAGER') or is_granted('ROLE_ADMIN')")
     */
    public function listAction(UserRepository $repo, Request $request)
    {
        $offset = intval($request->get('offset')) ?: 0;
        $limit = intval($request->get('limit')) ?: null;
        
        $users = array_filter($repo->getAll($limit, $offset), function($user)
        {
            return $user->isActive();
        });

        $response = $this->response([
            'users' => array_map(function($user)
            {
                /** @var User $user */

                return [
                    'id' => $user->getId(),
                    'username' => $user->getUsername(),
                    'email' => $user->getEmail(),
                    'roles' => explode(',', $user->getEntity()->roles),
                    'registered' => $user->getDateRegistered() ? $user->getDateRegistered()->format(\DateTime::ISO8601) : '',
                    'login' => $user->getLastLoginDate() ? $user->getLastLoginDate()->format(\DateTime::ISO8601) : '',
                    'grants' => $user->getGrants()
                ];
            }, $users)
        ]);
        
        return $response;
    }

    /**
     * @Route("/user/roles", methods={"POST"})
     * @Security("is_granted('ROLE_MANAGER') or is_granted('ROLE_ADMIN')")
     */
    public function rolesAction(UserRepository $repo, Request $request)
    {
        $id = $request->get('userId');
        if(!$id)
        {
            throw new MissingArgumentException('userId');
        }

        /** @var User $user */
        $user = $repo->getById($id);
        if(!$user)
        {
            throw new InvalidArgumentException('userId');
        }

        // Admin user override safeguard
        if(!$this->isGranted('ROLE_ADMIN') && (in_array('ROLE_ADMIN', $user->getRoles()) || in_array('ROLE_MANAGER', $user->getRoles())))
        {
            throw new InvalidArgumentException('userId');
        }

        $role = trim($request->get('role'));
        if(!$role)
        {
            throw new MissingArgumentException('role');
        }

        // Admin role override safeguard
        if(!$this->isGranted('ROLE_ADMIN') && ($role == 'ROLE_MANAGER' || $role == 'ROLE_ADMIN'))
        {
            throw new InvalidArgumentException('role');
        }

        try
        {
            $user->setRoles([$role]);
        }
        catch(\InvalidArgumentException $ex)
        {
            throw new InvalidArgumentException('role');
        }
        $this->flush();

        return $this->response(['roles' => array_map(function($role){ return $role->getRole(); }, $user->getRoles())]);
    }

    /**
     * @Route("/user/register", methods={"POST"})
     */
    public function registerAction(Request $request, UserRepository $repo, UserFactory $userFactory, TokenFactory $tokenFactory, Mailer $mailer)
    {
        if(!$email = trim($request->get('email')))
        {
            throw new MissingArgumentException('email');
        }

        if(!$username = trim($request->get('username')))
        {
            throw new MissingArgumentException('username');
        }

        if(!$password = $request->get('password'))
        {
            throw new MissingArgumentException('password');
        }

        if(!$backlink = $request->get('backlink'))
        {
            throw new MissingArgumentException('backlink');
        }

        if($user = $repo->getByEmail($email))
        {
            throw new ExistingUserException($user->isActive());
        }

        // Create the user
        $user = $userFactory->create($email, $username, $password, ['ROLE_USER']);
        $this->flush();

        // Create an activation token
        $tokenFactory->create(TokenFactory::TOKEN_USER_ACTIVATION, $backlink, $user);
        $this->flush();

        $mailer->sendAll();
        return $this->response();
    }

    /**
     * @Route("/user/activation-resend", methods={"POST"})
     */
    public function resendActivationEmail(TokenFactory $factory, Mailer $mailer, Request $request, UserRepository $repo)
    {
        if(!$email = $request->get('email'))
        {
            throw new MissingArgumentException('email');
        }

        if(!$backlink = (string)$request->get('backlink'))
        {
            throw new MissingArgumentException('backlink');
        }

        if(!$user = $repo->getByEmail($email))
        {
            throw new InvalidArgumentException('email');
        }

        if($user->isActive())
        {
            throw new InvalidArgumentException('email');
        }

        // Create the activation token
        $factory->create(TokenFactory::TOKEN_USER_ACTIVATION, $backlink, $user);
        $this->flush();

        $mailer->sendAll();

        return $this->response();
    }

    /**
     * @Route("/user/activate", methods={"POST"})
     */
    public function activateAccountAction(Request $request, TokenRepository $tokenRepo)
    {
        if(!$hash = $request->get('token'))
        {
            throw new MissingArgumentException('token');
        }

        $token = $tokenRepo->getByHash(TokenRepository::TOKEN_USER_ACTIVATION, $hash);
        if(!$token)
        {
            throw new InvalidArgumentException('token');
        }
        else if(!$token->isValid())
        {
            throw new TokenExpiredException('token');
        }

        $token->getUser()->activate();
        $token->markAsUsed();

        $this->flush();
        return $this->response();
    }

    /**
     * @Route("/user/reset/issue", methods={"POST"})
     */
    public function issueResetTokenAction(Request $request, UserRepository $repo, TokenFactory $factory, Mailer $mailer)
    {
        if(!$backlink = (string)$request->get('backlink'))
        {
            throw new MissingArgumentException('backlink');
        }

        if(!$email = $request->get('email'))
        {
            throw new MissingArgumentException('email');
        }

        if(!$user = $repo->getByEmail($email))
        {
            throw new InvalidArgumentException('email');
        }

        $token = $factory->create(TokenFactory::TOKEN_PASSWORD_RESET, $backlink, $user);
        $this->flush();
        $mailer->sendAll();

        return $this->response();
    }


    /**
     * @Route("/user/reset/update", methods={"POST"})
     */
    public function passwordResetAction(Request $request, TokenRepository $tokenRepo)
    {
        if(!$hash = $request->get('token'))
        {
            throw new MissingArgumentException('token');
        }

        $token = $tokenRepo->getByHash(TokenRepository::TOKEN_PASSWORD_RESET, $hash);
        if(!$token)
        {
            throw new InvalidArgumentException('token');
        }
        else if(!$token->isValid())
        {
            throw new TokenExpiredException('token');
        }

        if(!$password = $request->get('password'))
        {
            throw new MissingArgumentException('password');
        }

        $user = $token->getUser();
        $user->updatePassword($password);
        $token->markAsUsed();

        $this->flush();

        return $this->response();
    }

    


}

