<?php

namespace Lc\SovBundle\Controller;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Config\Option\EA;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController as EaAbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityDeletedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityUpdatedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException;
use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException;
use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory;
use EasyCorp\Bundle\EasyAdminBundle\Factory\FilterFactory;
use EasyCorp\Bundle\EasyAdminBundle\Factory\PaginatorFactory;
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use EasyCorp\Bundle\EasyAdminBundle\Security\Permission;
use Lc\SovBundle\Doctrine\EntityManager;
use Lc\SovBundle\Doctrine\Extension\DevAliasInterface;
use Lc\SovBundle\Doctrine\Extension\SeoInterface;
use Lc\SovBundle\Doctrine\Extension\SortableInterface;
use Lc\SovBundle\Doctrine\Extension\TranslatableInterface;
use Lc\SovBundle\Doctrine\Extension\TreeInterface;
use Lc\SovBundle\Field\CollectionField;
use Lc\SovBundle\Field\GalleryManagerField;
use Lc\SovBundle\Form\Type\Crud\PositionType;
use Lc\SovBundle\Translation\TranslatorAdmin;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;


abstract class AbstractAdminController extends EaAbstractCrudController
{
    protected $session;
    protected $request;
    protected $em;
    protected $translatorAdmin;

    public function __construct(
        SessionInterface $session,
        RequestStack $request,
        EntityManager $em,
        TranslatorAdmin $translatorAdmin
    )
    {
        $this->session = $session;
        $this->request = $request;
        $this->em = $em;
        $this->translatorAdmin = $translatorAdmin;
    }

    public function configureActions(Actions $actions): Actions
    {
        /* Translatable */
        if ($this->isInstanceOf(TranslatableInterface::class)) {
            $actions->update(
                Crud::PAGE_INDEX,
                Action::EDIT,
                function (Action $action) {
                    $action->setTemplatePath('@LcSov/adminlte/crud/action/translatable.html.twig');

                    return $action;
                }
            );
        }

        /* Boutons des actions dans les listes */
        $actionsArray[Crud::PAGE_INDEX] = [
            Action::NEW => [
                'icon' => 'plus',
                'label' => $this->translatorAdmin->transAction('create'),
                'add_class' => 'btn-sm'
            ],
            Action::EDIT => [
                'class' => 'btn btn-sm btn-primary',
                'icon' => 'edit',
                'label' => false,
                'html_attributes' => array(
                    'data-toggle' => 'tooltip',
                    'title' => $this->translatorAdmin->transAction('edit')
                )
            ],
            Action::DELETE => [
                'icon' => 'trash',
                'dropdown' => true,
                'label' => $this->translatorAdmin->transAction('delete')
            ],
            Action::BATCH_DELETE => [
                'class' => 'btn btn-sm btn-danger',
                'icon' => 'trash',
                'label' => $this->translatorAdmin->transAction('delete')
            ],
        ];

        /* Boutons des actions dans l'édition */

        $actionSaveAndReturn = [
            'add_class' => 'float-right',
            'icon' => 'check',
            'label' => $this->translatorAdmin->transAction('save_and_return')
        ];
        $actionIndex = [
            'icon' => 'chevron-left',
            'class' => 'btn btn-link',
            'label' => $this->translatorAdmin->transAction('back_index')
        ];

        $actionsArray[Crud::PAGE_EDIT] = [
            Action::SAVE_AND_CONTINUE => [
                'class' => 'btn btn-info float-right',
                'label' => $this->translatorAdmin->transAction('save_and_continue')
            ],
            Action::DELETE => [
                'icon' => 'trash',
                'class' => 'btn btn-outline-danger action-delete',
                'label' => $this->translatorAdmin->transAction('delete')
            ],
            Action::SAVE_AND_RETURN => $actionSaveAndReturn,
            Action::INDEX => $actionIndex,
        ];

        $actionsArray[Crud::PAGE_NEW] = [
            Action::SAVE_AND_ADD_ANOTHER => [
                'class' => 'btn btn-info float-right',
                'label' => $this->translatorAdmin->transAction('save_and_add_another')
            ],
            Action::SAVE_AND_RETURN => $actionSaveAndReturn,
            Action::INDEX => $actionIndex,
        ];

        $actions->add(Crud::PAGE_EDIT, Action::INDEX);
        $actions->add(Crud::PAGE_EDIT, Action::DELETE);
        $actions->add(Crud::PAGE_NEW, Action::INDEX);

        if ($this->isInstanceOf(SortableInterface::class)) {
            $sortAction = Action::new('sort', $this->translatorAdmin->transAction('sort'), 'fa fa-sort')
                ->linkToCrudAction('sort')
                ->setCssClass('btn btn-sm btn-success')
                ->createAsGlobalAction();

            $actions->add(Crud::PAGE_INDEX, $sortAction);
        }


        if ($this->isInstanceOf(TreeInterface::class)) {
            $indexChildAction = Action::new(
                'index_children',
                $this->translatorAdmin->transAction('index_children'),
                'fa fa-list'
            )
                ->linkToCrudAction(Action::INDEX)
                ->setLabel('')
                ->setHtmlAttributes(array('data-toggle' => 'tooltip', 'title' => 'Afficher les enfants'))
                ->setTemplatePath('@LcSov/adminlte/crud/action/index_children.html.twig')
                ->setCssClass('btn btn-sm btn-success');

            $backParentAction = Action::new(
                'index_parent',
                $this->translatorAdmin->transAction('index_parent'),
                'fa fa-chevron-left'
            )
                ->linkToCrudAction(Action::INDEX)
                ->setCssClass('btn btn-sm btn-info')
                ->createAsGlobalAction();

            $actions->add(Crud::PAGE_INDEX, $backParentAction);
            $actions->add(Crud::PAGE_INDEX, $indexChildAction);
        }

        $actions->reorder(Crud::PAGE_EDIT, [Action::INDEX, Action::SAVE_AND_RETURN, Action::SAVE_AND_CONTINUE]);
        $actions->reorder(Crud::PAGE_NEW, [Action::INDEX, Action::SAVE_AND_RETURN, Action::SAVE_AND_ADD_ANOTHER]);


        foreach ($actionsArray as $crudActionName => $actionsStyle) {
            foreach ($actionsStyle as $actionName => $button) {
                $actions->update(
                    $crudActionName,
                    $actionName,
                    function (Action $action) use ($button) {
                        if (isset($button['add_class'])) {
                            $action->addCssClass($button['add_class']);
                        }

                        if (isset($button['class'])) {
                            $action->setCssClass($button['class']);
                        }

                        if (isset($button['icon'])) {
                            $action->setIcon('fa fa-' . $button['icon']);
                        }

                        if (isset($button['label'])) {
                            $action->setLabel($button['label']);
                        }

                        if (isset($button['dropdown']) && $button['dropdown']) {
                            $action->addCssClass('in-dropdown');
                        }

                        if (isset($button['html_attributes']) && $button['html_attributes']) {
                            $action->setHtmlAttributes($button['html_attributes']);
                        }

                        return $action;
                    }
                );
            }
        }

        return $actions;
    }

    public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
    {
        if (Crud::PAGE_INDEX === $responseParameters->get('pageName')) {
            $responseParameters->set('fields', $this->configureFields('index'));
        }

        return $responseParameters;
    }

    public function configureCrud(Crud $crud): Crud
    {
        $crud = parent::configureCrud($crud);

        $this->setMaxResults($crud);
        if ($this->isInstanceOf(SortableInterface::class)) {
            $crud->setDefaultSort(['position' => 'ASC']);
        }
        return $crud;
    }

    public function setMaxResults(Crud $crud)
    {
        $entityClass = $this->getEntityFqcn();
        $paramListMaxResults = 'listMaxResults';
        $paramSessionListMaxResults = $entityClass . '-' . $paramListMaxResults;
        $requestListMaxResults = $this->request->getCurrentRequest()->get($paramListMaxResults);

        if ($requestListMaxResults) {
            $this->session->set($paramSessionListMaxResults, $requestListMaxResults);
        }
        $maxResults = $this->session->get($paramSessionListMaxResults) ? $this->session->get(
            $paramSessionListMaxResults
        ) : 30;

        $crud->setPaginatorPageSize($maxResults);
    }

    public function configureFields(string $pageName): iterable
    {
        $seoPanel = $confPanel = array();

        if ($this->isInstanceOf(SeoInterface::class)) {
            $seoPanel = [
                FormField::addPanel('seo')->setTemplateName('crud/field/generic'),
                TextField::new('metaTitle')->setLabel('Meta Title')->setHelp(
                    'Affiché dans les résultats de recherche Google'
                )->hideOnIndex(),
                TextareaField::new('metaDescription')->setLabel('Meta description')->setHelp(
                    'Affiché dans les résultats de recherche Google'
                )->hideOnIndex(),
                CollectionField::new('oldUrls')
                    ->setFormTypeOption('entry_type', TextType::class)->setLabel(
                        'Anciennes urls du document'
                    )->hideOnIndex(),
            ];
        }

        if ($this->isInstanceOf(DevAliasInterface::class)) {
            $confPanel = [
                FormField::addPanel('configuration')->setTemplateName('crud/field/generic'),
                TextField::new('devAlias')->hideOnIndex(),
            ];
        }

        return array_merge($seoPanel, $confPanel);
    }


    public function sort(AdminContext $context)
    {
        $event = new BeforeCrudActionEvent($context);
        $this->get('event_dispatcher')->dispatch($event);
        if ($event->isPropagationStopped()) {
            return $event->getResponse();
        }

        if (!$this->isGranted(Permission::EA_EXECUTE_ACTION) || !$this->isInstanceOf(SortableInterface::class)) {
            throw new ForbiddenActionException($context);
        }

        $fields = FieldCollection::new($this->configureFields(Crud::PAGE_INDEX));
        $filters = $this->get(FilterFactory::class)->create(
            $context->getCrud()->getFiltersConfig(),
            $fields,
            $context->getEntity()
        );
        $queryBuilder = $this->createIndexQueryBuilder($context->getSearch(), $context->getEntity(), $fields, $filters);
        $paginator = $this->get(PaginatorFactory::class)->create($queryBuilder);

        $entities = $this->get(EntityFactory::class)->createCollection($context->getEntity(), $paginator->getResults());
        $this->get(EntityFactory::class)->processFieldsForAll($entities, $fields);

        $sortableForm = $this->createFormBuilder(array('entities', $paginator->getResults()))
            ->add(
                'entities',
                CollectionType::class,
                array(
                    'required' => true,
                    'allow_add' => true,
                    'entry_type' => PositionType::class,
                )
            )
            ->getForm();

        $entityManager = $this->getDoctrine()->getManagerForClass($this->getEntityFqcn());
        $repository = $entityManager->getRepository($this->getEntityFqcn());

        $sortableForm->handleRequest($context->getRequest());

        if ($sortableForm->isSubmitted() && $sortableForm->isValid()) {
            foreach ($sortableForm->get('entities')->getData() as $elm) {
                $entityInstance = $repository->find($elm['id']);
                $entityDto = $context->getEntity()->newWithInstance($entityInstance);

                if (!$entityDto->isAccessible()) {
                    throw new InsufficientEntityPermissionException($context);
                }

                $event = new BeforeEntityDeletedEvent($entityInstance);
                $this->get('event_dispatcher')->dispatch($event);
                $entityInstance = $event->getEntityInstance();

                $entityInstance->setPosition($elm['position']);
                $this->updateEntity($entityManager, $entityInstance);

                $this->get('event_dispatcher')->dispatch(new AfterEntityUpdatedEvent($entityInstance));
            }

            $url = $this->get(AdminUrlGenerator::class)
                ->setAction(Action::INDEX)
                ->generateUrl();
            $this->addFlash('success', $this->translatorAdmin->transFlashMessage('sort'), array());

            return $this->redirect($url);
        }

        $responseParameters = $this->configureResponseParameters(
            KeyValueStore::new(
                [
                    'pageName' => Crud::PAGE_INDEX,
                    'templatePath' => '@LcSov/adminlte/crud/sort.html.twig',
                    'entities' => $entities,
                    'paginator' => $paginator,
                    'global_actions' => array(),
                    'batch_actions' => array(),
                    'filters' => $filters,
                    'sortable_form' => $sortableForm,
                ]
            )
        );
        $responseParameters->set('fields', $this->configureFields('index'));
        $event = new AfterCrudActionEvent($context, $responseParameters);
        $this->get('event_dispatcher')->dispatch($event);
        if ($event->isPropagationStopped()) {
            return $event->getResponse();
        }

        return $responseParameters;
    }

    public function createIndexQueryBuilder(
            SearchDto $searchDto,
            EntityDto $entityDto,
            FieldCollection $fields,
            FilterCollection $filters
    ): QueryBuilder {
        $queryBuilder = parent::createIndexQueryBuilder(
                $searchDto,
                $entityDto,
                $fields,
                $filters
        );

        dump(get_defined_vars());
        if ($this->isInstanceOf(TreeInterface::class)) {
            $entityId = $searchDto->getRequest()->get('entityId');
            if ($entityId !== null) {
                $queryBuilder->andWhere('entity.parent = :entityId');
                $queryBuilder->setParameter('entityId', $searchDto->getRequest()->get('entityId'));
            } else {
                $queryBuilder->andWhere('entity.parent IS NULL');
            }
        }


        return $queryBuilder;
    }

    public function createSortQueryBuilder(
            SearchDto $searchDto,
            EntityDto $entityDto,
            FieldCollection $fields,
            FilterCollection $filters
    ): QueryBuilder {

        $queryBuilder = $this->createIndexQueryBuilder($searchDto,$entityDto,$fields,$filters);

        return $queryBuilder;
    }

    public function edit(AdminContext $context)
    {
        $response = parent::edit($context);;

        // on vide le flash bag si édition en ajax (notification déjà affichée en Javascript)
        if ($context->getRequest()->isXmlHttpRequest()) {
            $this->session->getFlashBag()->clear();
        }

        return $response;
    }

    public function isInstanceOf(string $interfaceName): bool
    {
        return in_array($interfaceName, class_implements($this->getEntityFqcn()));
    }


    public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
    {
        $entityManager->update($entityInstance);
        $entityManager->flush();
    }

    public function persistEntity(EntityManagerInterface $entityManager, $entityInstance): void
    {
        $entityManager->create($entityInstance);
        $entityManager->flush();
    }

}