<?php

namespace App\Controller;

use App\Entity\User;
use App\Entity\Comment;
use App\Entity\Document;
use App\Entity\Directory;
use App\Form\CommentType;
use App\Form\SearchBarType;
use Psr\Log\LoggerInterface;
use App\Repository\DocumentRepository;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Gedmo\Loggable\Entity\Repository\LogEntryRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

#[Route('/document', name: 'document_')]
class DocumentController extends AbstractController
{
    private TokenStorageInterface $TokenStorage;
    private LoggerInterface $logger;

    public function __construct(TokenStorageInterface $TokenStorage, LoggerInterface $logger)
    {
        $this->logger = $logger;
        $this->TokenStorage = $TokenStorage;
    }

    #[Route('/archive/{id}', name: 'archive')]
    #[IsGranted('archive', subject: 'Document', message: 'granted_not_allowed_archiving_document')]
    public function archive(Document $Document, Request $Request): Response
    {
        $entityManager = $this->getDoctrine()->getManager();
        $Document->setArchive(($Document->getArchive()) ? false : true);
        $entityManager->persist($Document);

        try {
            $entityManager->flush();
        } catch (\Throwable $th) {
            if ($_ENV['APP_ENV'] === 'dev') {
                throw $th; //DEBUG
            }

            $this->addFlash(
                'warning',
                'alert_error_' . (($Document->getArchive()) ? 'archiving' : 'unarchiving') . '_document'
            );
            if (null != $Request->headers->get('referer')) {
                return $this->redirect($Request->headers->get('referer'));
            }
            return $this->redirectToRoute('document_view', ['id' => $Document->getId()]);
        }

        $this->addFlash(
            'success',
            'alert_success_' . (($Document->getArchive()) ? 'archiving' : 'unarchiving') . '_document'
        );
        if (null != $Request->headers->get('referer')) {
            return $this->redirect($Request->headers->get('referer'));
        }
        return $this->redirectToRoute('document_view', ['id' => $Document->getId()]);
    }


    #[Route('/view/{id}', name: 'view')]
    #[IsGranted('view', subject: 'Document', message: 'granted_not_allowed_viewing_document')]
    public function view(Document $Document, EntityManagerInterface $em): Response
    {
        $formComment = $this->createForm(
            CommentType::class,
            new Comment(),
            [ 'action' => $this->generateUrl('comment_create', ['Document' => $Document->getId()])]
        );
        /**
         * @var LogEntryRepository $logEntryRepository
         */
        $logEntryRepository = $em->getRepository('Gedmo\Loggable\Entity\LogEntry');
        $documentHistory = $logEntryRepository->getLogEntries($Document);
        return $this->render('document/view.html.twig', [
            'controller_name' => 'DocumentController',
            'document' => $Document,
            'history' => $documentHistory,
            'formComment' => $formComment->createView()
        ]);
    }


    #[Route('/history/{id}', name: 'history')]
    #[IsGranted('view', subject: 'Document', message: 'granted_not_allowed_viewing_document')]
    public function history(Document $Document, EntityManagerInterface $em): Response
    {

        /**
         * @var LogEntryRepository $logEntryRepository
         */
        $logEntryRepository = $em->getRepository('Gedmo\Loggable\Entity\LogEntry');
        $documentHistory = $logEntryRepository->getLogEntries($Document);
        return $this->render('document/history.html.twig', [
            'controller_name' => 'DocumentController',
            'document' => $Document,
            'history' => $documentHistory,
        ]);
    }


    #[Route('/create/{type}', name: 'create')]
    #[Route('/create/{type}/directory/{directory?}', name: 'create_directory')]
    #[Route('/create/{type}/user/{user?}', name: 'create_user')]
    public function create(string $type, Request $request, Directory $directory = null, User $user = null): Response
    {

        $Document = $this->getDocumentByTypename($type);
        if (!$this->IsGranted('create', $Document)) {
            throw new AccessDeniedHttpException('granted_not_allowed_creating_document');
        }

        if (method_exists($Document, 'setDirectory')) {
            if (is_null($directory)) {
                throw new \Exception('exception_document_need_directory');
            }
            $Document->setDirectory($directory);
        }

        if (method_exists($Document, 'setUser')) {
            if (is_null($user)) {
                throw new \Exception('exception_document_need_user');
            }
            $Document->setUser($user);
        }

        $form = $this->getFormByDocumentType($Document);
        $form->handleRequest($request);

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

            /**
             * @var User $loggedUser
             */
            $loggedUser = $this->getUser();

            //check subGroups
            //check for subGroups addition and user not in thoses subgroups

            foreach ($Document->getAllowedSubGroups() as $key => $subGroup) {
                if (
                    !$loggedUser->getSubGroups()->contains($subGroup)
                    && !$loggedUser->hasPermission('group_ignore_subgroups')
                ) {
                    $this->addFlash('danger', 'alert_danger_adding_subgroup_document');
                    $Document->removeAllowedSubGroup($subGroup);
                }
            }


            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($Document);
            try {
                $entityManager->flush();
            } catch (\Throwable $th) {
                if ($_ENV['APP_ENV'] === 'dev') {
                    throw $th; //DEBUG
                } else {
                    $this->logger->error($th);
                }

                $this->addFlash('danger', 'alert_error_creating_document');
                return $this->redirectToRoute($request->getRequestUri());
            }

            $this->addFlash('success', 'alert_success_creating_document');
            return $this->redirectToRoute('document_view', ['id' => $Document->getId()]);
        }

        return $this->render('document/create.html.twig', [
            'controller_name' => 'DocumentController',
            'document' => $Document,
            'directory' => $directory,
            'form' => $form->createView(),
            'type' => $type,
        ]);
    }


    #[Route('/edit/{id}', name: 'edit')]
    #[IsGranted('edit', subject: 'Document', message: 'granted_not_allowed_editing_document')]
    public function edit(Request $request, Document $Document): Response
    {


        $CurrentSubGroupsAlloweds = $Document->getAllowedSubGroups()->toArray();

        $form = $this->getFormByDocumentType($Document);
        $form->handleRequest($request);



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

            /**
             * @var User $User
             */
            $User = $this->getUser();

            //check subGroups

            //check for missing subGroups
            foreach ($CurrentSubGroupsAlloweds as $key => $subGroup) {
                if (
                    !in_array($subGroup, $Document->getAllowedSubGroups()->toArray())
                    && !$User->getSubGroups()->contains($subGroup)
                    && !$User->hasPermission('group_ignore_subgroups')
                ) {
                    $this->addFlash('danger', 'alert_danger_removing_subgroup_document');
                    $Document->addAllowedSubGroup($subGroup);
                }
            }

            //check for subGroups addition and user not in thoses subgroups

            foreach ($Document->getAllowedSubGroups() as $key => $subGroup) {
                if (
                    !in_array($subGroup, $CurrentSubGroupsAlloweds)
                    && !$User->getSubGroups()->contains($subGroup)
                    && !$User->hasPermission('group_ignore_subgroups')
                ) {
                    $this->addFlash('danger', 'alert_danger_adding_subgroup_document');
                    $Document->removeAllowedSubGroup($subGroup);
                }
            }


            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($Document);
            try {
                $entityManager->flush();
            } catch (\Throwable $th) {
                if ($_ENV['APP_ENV'] === 'dev') {
                    throw $th; //DEBUG
                } else {
                    $this->logger->error($th);
                }

                $this->addFlash('danger', 'alert_error_editing_document');
                return $this->redirectToRoute($request->getRequestUri());
            }

            $this->addFlash('success', 'alert_success_editing_document');
            return $this->redirectToRoute('document_view', ['id' => $Document->getId()]);
        }
        return $this->render('document/edit.html.twig', [
            'controller_name' => 'DocumentController',
            'document' => $Document,
            'form' => $form->createView()
        ]);
    }

    #[Route('/delete/{id}', name: 'delete')]
    #[IsGranted('delete', subject: 'Document', message: 'granted_not_allowed_deleting_document')]
    public function delete(Document $Document, Request $Request): Response
    {
        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->remove($Document);
        try {
            $entityManager->flush();
        } catch (\Throwable $th) {
            if ($_ENV['APP_ENV'] === 'dev') {
                throw $th; //DEBUG
            }
            $this->addFlash('warning', 'alert_error_deleting_document');
            if (null != $Request->headers->get('referer')) {
                return $this->redirect($Request->headers->get('referer'));
            }
            return $this->redirectToRoute('document_view', ['id' => $Document->getId()]);
        }

        $this->addFlash('success', 'alert_success_deleting_document');
        /*if (null != $Request->headers->get('referer')) {
            return $this->redirect($Request->headers->get('referer'));
        }*/ //TODO: fix refere document when deleted (if path return view, send to list)
        return $this->redirectToRoute('document_list', ['type' => $Document->getClassShort()]);
    }

    #[Route('/archives/{type}/{directory?}', name: 'list_archives')]
    #[Route('/{type}/{directory?}', name: 'list')]
    public function list(
        PaginatorInterface $paginator,
        Request $request,
        DocumentRepository $DocumentRepository,
        EntityManagerInterface $EntityManagerInterface,
        Directory $directory = null,
        string $type
    ): Response {
        $searchForm = $this->createForm(SearchBarType::class);
        $searchForm->handleRequest($request);
        $documentTypeEntity = $this->getDocumentByTypename($type);
        /**
         * @var DocumentRepository $DocumentRepository
         */
        $DocumentRepository = $EntityManagerInterface->getRepository(get_class($documentTypeEntity));
        $archive = ($request->attributes->get('_route') == 'document_list_archives');
        $pagination = $paginator->paginate(
            $DocumentRepository ->listForUser($this->getUser(), $documentTypeEntity->getIsPublic())
                                ->search((
                                    $searchForm->isSubmitted()
                                    && $searchForm->isValid()
                                    && $searchForm->getData()['subject'] !== null
                                ) ? $searchForm->getData()['subject'] : null)
                                ->limitType($type)
                                ->archive($archive)
                                ->order(['createdAt' => 'DESC'])
                                ->limitDirectory($directory)
                                ->getResult(),
            $request->query->getInt('page', 1)
        );

        return $this->render('document/list.html.twig', [
            'controller_name' => 'DocumentController',
            'pagination' => $pagination,
            'count' => $pagination->getTotalItemCount(),
            'searchForm' => $searchForm->createView(),
            'documentTypeEntity' => $documentTypeEntity,
            'type' => $documentTypeEntity->getClassShort(),
            'archive' => $archive,
            'directory' => $directory
        ]);
    }


    private function getFormByDocumentType(Document $Document)
    {

        if (!is_subclass_of($Document, 'App\Entity\Document')) {
            throw new \Exception('exception_invalid_document_type');
        }

        $type = 'App\Form\\' . ucfirst(strtolower($Document->getClassShort())) . 'Type';
        if (!class_exists($type)) {
            throw new \Exception('exception_invalid_document_type');
            return null;
        }
        $formtype = new $type($this->TokenStorage);
        return $this->createForm(get_class($formtype), $Document);
    }

    private function getDocumentByTypename(string $type)
    {

        $type = 'App\Entity\\' . ucfirst(strtolower($type));
        if (!class_exists($type)) {
            throw new \Exception('exception_invalid_document_type');
            return null;
        }
        $Document = new $type($this->getUser());
        if (!is_subclass_of($Document, 'App\Entity\Document')) {
            throw new \Exception('exception_invalid_document_type');
        }

        return $Document ;
    }
}
