<?php

namespace App\Controller;

use App\Entity\User;
use App\Form\UserType;
use App\Service\DataGrid\DataGrid;
use App\Service\DataGrid\DataGridFilterMultiselect;
use App\Service\DataGrid\DataGridFilterQuery;
use App\Service\DataGrid\DataGridFilterSingleselect;
use App\Service\DataGrid\DataGridHeader;
use App\Service\DataGrid\DataGridNavigation;
use App\Service\DataGrid\DataGridSort;
use App\Service\UserService;
use Doctrine\ORM\EntityNotFoundException;
use FOS\RestBundle\Controller\AbstractFOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Limenius\Liform\Liform;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

/**
 * Class UserController.
 */
class UserController extends AbstractFOSRestController
{
    private $userService;
    private $encoder;

    public function __construct(UserService $userService, UserPasswordEncoderInterface $encoder)
    {
        $this->userService = $userService;
        $this->encoder = $encoder;
    }

    /**
     * Retrieves a collection of User resource.
     *
     * @Rest\Get("/user/grid")
     * @Rest\View(serializerGroups={"grid"})
     *
     * @param Request $request
     *
     * @return View
     */
    public function getGridUser(Request $request): View
    {
        $dataGrid = new DataGrid();

        $dataGrid
            ->setName('user')
            ->addHeader(new DataGridHeader(['label' => 'First Name', 'id' => 'firstName', 'isSortable' => true]))
            ->addHeader(new DataGridHeader(['label' => 'Last Name', 'id' => 'lastName', 'isSortable' => true]))
            ->addHeader(new DataGridHeader(['label' => 'Job Title', 'id' => 'jobTitle', 'isSortable' => true]))
            ->addHeader(new DataGridHeader(['label' => 'Email', 'id' => 'email', 'type' => 'email']))
            ->addHeader(new DataGridHeader(['label' => 'Is Active?', 'id' => 'active', 'isSortable' => true, 'type' => 'bool']));

        $dataGrid->setNavigation(new DataGridNavigation(['page' => $request->get('page'), 'rpp' => $request->get('rpp')]));

        $dataGrid->addSorter(new DataGridSort(['sort' => $request->get('sort'), 'order' => $request->get('order')]));

        $dataGrid->addFilter(new DataGridFilterQuery($request->get('q')));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'job_title',
            ['Content Writer', 'Technical', 'PPC', 'Account Manager', 'Director'],
            $request->get('job_title') ?? []));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'campaign',
            [],
            $request->get('campaign') ?? []));

        $dataGrid->addFilter(new DataGridFilterSingleselect(
            'active',
            [],
            null !== $request->get('active') ? filter_var($request->get('active'), FILTER_VALIDATE_BOOLEAN) : null));

        $dataGrid->setData($this->userService->getUsersByGrid($dataGrid))
            ->setTotal($this->userService->countUsersByGrid($dataGrid));

        // In case our GET was a success we need to return a 200 HTTP OK response with the collection of article object
        return View::create($dataGrid, Response::HTTP_OK);
    }

    /**
     * Retrieves a collection of User resource.
     *
     * @Rest\Get("/user")
     *
     * @param Request $request
     *
     * @return View
     */
    public function getAllUser(Request $request): View
    {
        $users = $this->userService->getAllUsers();

        // In case our GET was a success we need to return a 200 HTTP OK response with the collection of article object
        return View::create($users, Response::HTTP_OK);
    }

    /**
     * Retrieves an user resource.
     *
     * @Rest\Get("/user/{userId}", requirements={"userId"="\d+"}))
     *
     * @param int $userId
     *
     * @return View
     */
    public function getUserResource(int $userId): View
    {
        try {
            $user = $this->userService->getUser($userId);

            return View::create($user, Response::HTTP_OK);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }
    }

    /**
     * Retrieves an authenticated user resource.
     *
     * @Rest\Get("/user/self"))
     * @Rest\View(serializerEnableMaxDepthChecks=true, serializerGroups={"self"})
     *
     * @return View
     */
    public function getAuthenticatedUser(): View
    {
        return View::create($this->getUser(), Response::HTTP_OK);
    }

    /**
     * Creates an user resource.
     *
     * @Rest\Post("/user")
     *
     * @param Request $request
     *
     * @return View
     */
    public function postUser(Request $request): View
    {
        $form = $this->createForm(UserType::class, null, ['csrf_protection' => false]);

        $form->submit($request->request->all());

        if ($form->isSubmitted() && $form->isValid()) {
            $user = $form->getData();
            $user->setPlainPassword($request->get('plainPassword'));
            $user->setActive(true);
            $this->userService->saveUser($user);
            // In case our POST was a success we need to return a 201 HTTP CREATED response
            return View::create($user, Response::HTTP_CREATED);
        }

        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Replaces User resource.
     *
     * @Rest\Put("/user/{userId}")
     *
     * @param Request $request
     * @param int     $userId
     *
     * @return View
     */
    public function putUser(Request $request, int $userId): View
    {
        try {
            $user = $this->userService->getUser($userId);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }

        $form = $this->createForm(UserType::class, null, ['csrf_protection' => false]);
        $form->submit($request->request->all());

        if ($form->isSubmitted() && $form->isValid()) {
            $user = $form->getData();
            if ($request->get('plainPassword')) {
                $user->setPlainPassword($request->get('plainPassword'));
                $user->setPassword($this->encoder->encodePassword($user, $request->get('plainPassword')));
            }
            $this->userService->saveUser($user);
            // In case our POST was a success we need to return a 201 HTTP CREATED response
            return View::create($user, Response::HTTP_OK);
        }

        // In case our PUT was a success we need to return a 200 HTTP OK response with the object as a result of PUT
        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Updates User resource.
     *
     * @Rest\Patch("/user/{userId}")
     *
     * @param Request $request
     * @param int     $userId
     *
     * @return View
     */
    public function patchUserAction(Request $request, int $userId): View
    {
        try {
            $user = $this->userService->getUser($userId);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }

        $form = $this->createForm(UserType::class, $user);
        $form->submit($request->request->all(), false);

        if ($form->isSubmitted() && $form->isValid()) {
            $user = $form->getData();
            if (!$request->request->get('client')) {
                $user->setClient(null);
            }

            $user = $this->userService->saveUser($user);

            // In case our PUT was a success we need to return a 200 HTTP OK response with the object as a result of PATCH
            return View::create($user, Response::HTTP_OK);
        }

        // In case our PATCH wasn't a success we need to return a 400 HTTP BAD REQUEST response with the form object as a result
        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Removes the User resource.
     *
     * @Rest\Delete("/user/{userId}")
     *
     * @param int $userId
     *
     * @return View
     *
     * @throws \Doctrine\ORM\EntityNotFoundException
     */
    public function deleteUser(int $userId): View
    {
        try {
            $user = $this->userService->getUser($userId);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_BAD_REQUEST, 'message' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
        }

        $this->userService->deleteUser($user);
        // In case our DELETE was a success we need to return a 204 HTTP NO CONTENT response. The object is deleted.
        return View::create([], Response::HTTP_NO_CONTENT);
    }

    /**
     * Return serialized form to create resource.
     *
     * @Rest\Get("/user/schema")
     *
     * @param Liform $liform
     *
     * @return View
     */
    public function getSchema(Liform $liform): View
    {
        $form = $this->createForm(UserType::class, new User(), ['csrf_protection' => false])
            ->remove('avatar')
            ->remove('active');

        $form = $liform->transform($form);

        return View::create($form, Response::HTTP_OK);
    }
}
