<?php

namespace Comitium5\DesignerBundle\Controller\Widget;

use Comitium5\DesignerBundle\Controller\CRUDController;
use Comitium5\DesignerBundle\Entity\Widget;
use Comitium5\DesignerBundle\Entity\WidgetFavorite;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Exception\InvalidParameterException;

/**
 * Class WidgetController
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Controller\Widget
 */
class WidgetController extends CRUDController
{
    /**
     * @param  Request                                    $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function listAction(Request $request)
    {
        $this->checkRequest($request);

        $params = [];
        $masterRequest = $this->get('request_stack')->getMasterRequest();

        $ip = $masterRequest->headers->get('X-Forwarded-For');

        if ($ip === null) {
            $ip = $masterRequest->getClientIp();
        }

        if (!in_array($ip, $this->container->getParameter('allowed_widgets_ip'))) {
            $params = [
                'standard' =>  false,
            ];
        }

        $builder = $this->get('cs.manager.search.search_list')->handleAdvancedSearch(
            $this->get('cs.repository.widget'),
            $this->buildForm(),
            $request,
            $params
        );
        //        ldd($builder->getQuery()->getSQL());
        $pagination = $this->paginate($request, $builder);

        return $this->render('AdminBundle:Widget:page.html.twig', array(
            'pagination' => $pagination,
        ));
    }

    /**
     * @param  Request                                                                                       $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function createAction(Request $request)
    {
        $manager = $this->get('cs.manager.widget');

        $entity = $manager->getFactory()->create();

        $form = $this->createForm(WidgetType::class, $entity, array(
            'method' => Request::METHOD_POST,
            'validation_groups' => 'new',
            'data_class' => $manager->getFactory()->getEntityNamespace(),
        ));

        $form->handleRequest($request);

        if ($form->isValid()) {
            $manager->save($entity);

            $this->addToastrMessage('success', "widget.messages.success.create");

            return $this->handleFormRedirect($form, $this->generateUrl('admin_widget_index'));
        }

        return $this->render('AdminBundle:Widget:form.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView(),
            'action' => 'create',
        ));
    }

    /**
     * @param Request $request
     * @param Widget  $entity
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
     * @throws \Doctrine\ORM\EntityNotFoundException
     */
    public function editAction(Request $request, Widget $entity = null)
    {
        $this->handleGrantedGenericAccess(VoterInterface::EDIT, $entity);

        $manager = $this->get('cs.manager.widget');

        $form = $this->createForm(WidgetType::class, $entity, array(
            'method' => Request::METHOD_POST,
            'validation_groups' => 'edit',
            'data_class' => $manager->getFactory()->getEntityNamespace(),
        ));

        $form->handleRequest($request);

        if ($form->isValid()) {
            $state = $this->get("doctrine.orm.default_entity_manager")
                ->getRepository('ComitiumSuite\Components\Workflow\Entity\State')
                ->findByContentTypeAndBehaviour(ContentType::WIDGETS, StateBehaviours::STAND_BY)
                ->getQuery()
                ->getOneOrNullResult();

            if ($state) {
                $entity->setState($state);
            }

            if ($entity->getSource() !== null) {
                $entity->setUpdatedAt(new \DateTime());
            }

            $manager->update($entity);

            $this->addToastrMessage('success', "widget.messages.success.update");

            return $this->handleFormRedirect($form, $this->generateUrl('admin_widget_index'));
        }

        return $this->render('AdminBundle:Widget:form.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView(),
            'action' => 'edit',
        ));
    }

    /**
     * @param Request $request
     * @param Widget  $entity
     *
     * @return JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response
     * @throws \Doctrine\ORM\EntityNotFoundException
     */
    public function deleteAction(Request $request, Widget $entity = null)
    {
        $this->handleGrantedGenericAccess(VoterInterface::DELETE, $entity);

        $manager = $this->get('cs.manager.widget');

        if ($request->isMethod(Request::METHOD_GET)) {
            if ($this->checkDeletion($entity)) {
                $title   = $this->get('translator')->trans('widget.messages.delete.has_associations_title');
                $message = $this->get('translator')->trans('widget.messages.delete.has_associations');

                return $this->responseAvoidDeletion($title, $message);
            }

            return new Response();
        }

        if (!$entity) {
            return $this->redirectToRoute('admin_widget_index');
        }

        $manager->remove($entity);

        $this->addToastrMessage('success', "widget.messages.success.delete");

        return $this->redirectToRoute('admin_widget_index');
    }

    /**
     * @param Request $request
     *
     * @return JsonResponse
     */
    public function bulkDeleteAction(Request $request)
    {
        $response = new JsonResponse();
        $status = JsonResponse::HTTP_OK;
        $message = '';
        $manager = $this->get('cs.manager.widget');

        $items = $this->processSelected($request,$manager->getRepository());

        if ($request->isMethod(Request::METHOD_GET)) {
            $avoid = false;

            foreach ($items as $item) {
                if (!$this->checkDeletion($item) || $this->handleGrantedGenericAccess(VoterInterface::DELETE, $item, false) === false) {
                    continue;
                }

                $avoid = true;
            }

            $title   = $this->get('translator')->trans('widget.messages.delete.has_associations_title');
            $message = $this->get('translator')->trans('widget.messages.delete.has_associations');

            return $avoid === true ? $this->responseAvoidDeletion($title, $message) : new Response();
        }

        if ($request->isMethod(Request::METHOD_POST)) {
            try {
                foreach ($items as $item) {
                    $manager->remove($item);
                }

                $this->addToastrMessage('success', 'widget.messages.success.delete', count($items) > 1 ? 2 : 1);
            } catch (Exception $e) {
                $status = JsonResponse::HTTP_INTERNAL_SERVER_ERROR;
            }
        }

        $response->setStatusCode($status);
        $response->setContent($message);

        return $response;
    }

    /**
     * @param Request $request
     * @param Widget  $entity
     *
     * @return Response
     */
    public function downLoadSkeletonAction(Request $request, Widget $entity = null)
    {
        return $zip = $this->get('cs.generator.widget')->generateZip($entity, true);
    }

    /**
     * @param Request $request
     * @param Widget  $entity
     *
     * @return Response
     */
    public function downLoadCodeAction(Request $request, Widget $entity = null)
    {
        return $this->get('cs.generator.widget')->generateZipFromSource($entity, true);
    }

    /**
     * @return JsonResponse|Response
     */
    private function checkDeletion(WidgetInterface $entity)
    {
        return $this->get('cs.manager.widget')->checkIfIsUsedByLayouts($entity);
    }

    /**
     * @param Request $request
     * @param Widget $widget
     *
     * @return WidgetController|JsonResponse|Response
     * @throws \Doctrine\ORM\ORMException
     */
    public function uploadAction(Request $request, Widget $widget)
    {
        $file = $request->files->get('file');

        if ($file === null || $widget === null) {
            return Response::create('', Response::HTTP_BAD_REQUEST);
        }

        if ($file->getClientOriginalExtension() !== "zip") {
            $message = $this
                ->get('translator')
                ->trans(
                    'widget.validator.extension_error',
                    ['%extension%' => $file->getClientOriginalExtension()]
                );

            return $this->errorUploadResponse(['message' => $message]);
        }

        $path = $this->container->getParameter('kernel.cache_dir');

        $movedFile = $file->move($path, $file->getClientOriginalName());

        $widget->setFile($movedFile);

        $validator = $this->get('cs.validator.widget');

        if ($validator->validateFolderName($widget) === false) {
            $message = $this
                ->get('translator')
                ->trans(
                    'widget.validator.folder_name',
                    ['%name%' => $widget->formatName()]
                );

            return $this->errorUploadResponse(['message' => $message]);
        }

        $translationErrors = $validator->validateTranslationFileName($widget, $this->findAllPossibleLocales());

        if (count($translationErrors) > 0) {
            $message = $this
                ->get('translator')
                ->trans(
                    'widget.validator.file_name',
                    [
                        '%name%'  => $widget->formatName(),
                        '%files%' => implode(",", $translationErrors)
                    ]
                );

            return $this->errorUploadResponse(['message' => $message]);
        }

        $validateCommand = $validator->validateCoding($widget);

        if ($validateCommand["valid"] === false) {
            if ($validateCommand["error"] === WidgetValidator::ERROR_COMMAND) {
                $message = $this->get('translator')->trans('widget.validator.valid_content');
            }

            if ($validateCommand["error"] === WidgetValidator::ERROR_CODING) {
                $message = $this->get('translator')->trans('widget.validator.codig_error');
            }

            if ($validateCommand["error"] === WidgetValidator::ERROR_JS) {
                $message = $this->get('translator')->trans('widget.validator.js_error');
            }

            if ($validateCommand["error"] === WidgetValidator::ERROR_CSS) {
                $message = $this->get('translator')->trans('widget.validator.css_error');
            }

            return $this->errorUploadResponse(['message' => $message]);
        }

        $manager = $this->get('cs.manager.widget');

        $data = $manager->compareFiles($widget, $movedFile);

        $status = JsonResponse::HTTP_OK;

        if (count($data)) {
            $data['message'] = $this->get('translator')->trans('widget.validator.override_content');
            $data['displayDiff'] = true;
            $data['html'] = $this->renderView('AdminBundle:Widget:diff-portlets.html.twig', [
                'data' => $data,
            ]);
            $status = JsonResponse::HTTP_BAD_REQUEST;
        }

        $data['path'] = $movedFile->getPathName();

        return JsonResponse::create($data, $status);
    }

    /**
     * @param array $data
     *
     * @return Response|static
     */
    private function errorUploadResponse(array $data)
    {
        return JsonResponse::create($data, Response::HTTP_BAD_REQUEST);
    }

    /**
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function advancedSearchFormAction()
    {
        return $this->render('AdminBundle:Widget:advanced-search.html.twig', array(
            'form' => $this->buildForm()->createView(),
            'search' => '',
        ));
    }

    /**
     * @return \Symfony\Component\Form\Form
     */
    private function buildForm()
    {
        return $this->createForm(WidgetAdvancedSearchType::class, null, array(
            'action' => $this->generateUrl('admin_widget_search'),
            'method' => 'GET',
        ));
    }

    /**
     * @param $attribute
     * @param WidgetInterface $entity
     * @param null $redirectRoute
     * @param bool|true $throwException
     */
    public function handleGrantedGenericAccess(
        $attribute,
        $entity,
        $redirectRoute = null,
        $throwException = true
    )
    {
        $this->handleIfNotFound($entity, $throwException);

        // Uncomment to subsite restrictions. By the moment widgets are shared between subsites
        /*if ($entity->getSubsite() !== null) {
            parent::handleGrantedGenericAccess($attribute, $entity, $redirectRoute, $throwException);
        }*/
    }

    /**
     * @param Request $request
     * @param Widget $widget
     *
     * @return JsonResponse
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function favoriteAction(Request $request, Widget $widget)
    {
        $favorite = $request->request->get("favorite") === "true" ? true : false;

        $em = $this->get("doctrine.orm.default_entity_manager");
        $repository = $this->get("designer.repository.widget_favorite");
        $user = $this->getUser();

        $widgetFavorite = $repository->findBy(array(
            "widget"  => $widget,
            "user"    => $user->getId(),
        ));

        if ($favorite === true && !$widgetFavorite) {
            $widgetFavorite = new WidgetFavorite($widget, $user->getId());
            $em->persist($widgetFavorite);
        } elseif ($favorite === false && $widgetFavorite instanceof WidgetFavorite) {
            $em->remove($widgetFavorite);
        } else {
            throw new InvalidParameterException();
        }

        $em->flush();

        return new JsonResponse();
    }

    /**
     * @return array
     */
    private function findAllPossibleLocales()
    {
        $locales = $this
            ->get('cs.repository.locale')
            ->findAll();

        return array_map(function (LocaleInterface $locale) {
            return $locale->getCode();
        }, $locales);
    }
}
