<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Controller\Admin;

use Gls\GlsPoland\AdePlus\Authorization\Fault\IncorrectCredentialsFault;
use Gls\GlsPoland\AdePlus\Authorization\Fault\SessionNotFoundFault;
use Gls\GlsPoland\Carrier\CarrierType;
use Gls\GlsPoland\Configuration\Api;
use Gls\GlsPoland\Configuration\Orders;
use Gls\GlsPoland\Configuration\SenderAddressConfigurationInterface;
use Gls\GlsPoland\Exception\UnableToSelectSenderAddressException;
use Gls\GlsPoland\Form\Type\ApiConfigurationType;
use Gls\GlsPoland\Form\Type\CarrierTypeChoiceType;
use Gls\GlsPoland\Form\Type\CreateCourierCarrierType;
use Gls\GlsPoland\Form\Type\CreateShopDeliveryCarrierType;
use Gls\GlsPoland\Form\Type\OrdersConfigurationType;
use Gls\GlsPoland\Form\Type\SenderDetailsType;
use Gls\GlsPoland\Message\UpdateApiConfigurationCommand;
use Gls\GlsPoland\PrestaShop\Grid\Definition\Factory\FilterableGridDefinitionFactoryInterface;
use Gls\GlsPoland\PrestaShop\Grid\Filters\CarrierFilters;
use Gls\GlsPoland\PrestaShop\Message\CreateCourierCarrierCommand;
use Gls\GlsPoland\PrestaShop\Message\CreateShopDeliveryCarrierCommand;
use Gls\GlsPoland\PrestaShop\Message\DeleteCarrierCommand;
use Gls\GlsPoland\Translation\TranslatorAwareTrait;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
use PrestaShop\PrestaShop\Core\Grid\Filter\GridFilterFormFactoryInterface;
use PrestaShop\PrestaShop\Core\Grid\GridFactoryInterface;
use PrestaShop\PrestaShop\Core\Grid\Presenter\GridPresenterInterface;
use PrestaShopBundle\Security\Voter\PageVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route(path="config", name="admin_gls_poland_config_", defaults={"_legacy_controller"=ConfigurationController::TAB_NAME, "_legacy_link"=ConfigurationController::TAB_NAME})
 */
final class ConfigurationController extends AbstractController
{
    use TranslatorAwareTrait;

    public const TAB_NAME = 'AdminGlsPolandConfiguration';
    public const CARRIERS_TAB_NAME = 'AdminCarriers';

    /**
     * @Route(path="/api", name="api", methods={"GET", "POST"})
     */
    public function apiConfig(Request $request, Api\PersistentConfigurationInterface $configuration, CommandBusInterface $bus): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::READ, self::TAB_NAME);

        $form = $this->createForm(ApiConfigurationType::class, $configuration->asNonPersistent(), [
            'disabled' => !$canUpdate = $this->isGranted(PageVoter::UPDATE, self::TAB_NAME),
        ]);

        if ($canUpdate && $form->handleRequest($request) && $form->isSubmitted() && $form->isValid()) {
            $bus->handle(new UpdateApiConfigurationCommand($form->getData()));
            $this->addFlash('success', $this->getTranslator()->trans('Successful update.', [], 'Admin.Notifications.Success'));

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

        return $this->render('@Modules/glspoland/views/templates/admin/config/api.html.twig', [
            'form' => $form->createView(),
            'can_update' => $canUpdate,
            'layoutTitle' => $this->getTranslator()->trans('API configuration', [], 'Modules.Glspoland.Configuration'),
            'headerTabContent' => $this->renderNav('api'),
            'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
        ]);
    }

    /**
     * @Route(path="/sender-details", name="sender_details", methods={"GET", "POST"})
     */
    public function senderDetailsConfig(Request $request, SenderAddressConfigurationInterface $configuration): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::READ, self::TAB_NAME);

        try {
            $form = $this->createForm(SenderDetailsType::class, $configuration->getSenderAddressDetails(), [
                'disabled' => !$canUpdate = $this->isGranted(PageVoter::UPDATE, self::TAB_NAME),
            ]);
        } catch (IncorrectCredentialsFault|SessionNotFoundFault $e) {
            $this->addFlash('warning', $this->getTranslator()->trans('API credentials are missing or invalid. Please update the API configuration before accessing the sender details configuration page.', [], 'Modules.Glspoland.Configuration'));

            return $this->redirectToRoute('admin_gls_poland_config_api');
        } catch (UnableToSelectSenderAddressException $e) {
            $this->addFlash('warning', $this->getTranslator()->trans('Selecting a sender address is not allowed for the configured API account.', [], 'Modules.Glspoland.Configuration'));
            $configuration->setSenderAddressDetails(null);

            return $this->render('@PrestaShop/Admin/layout.html.twig', [
                'layoutTitle' => $this->getTranslator()->trans('Sender details configuration', [], 'Modules.Glspoland.Configuration'),
                'headerTabContent' => $this->renderNav('sender_details'),
                'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
            ]);
        }

        if ($canUpdate && $form->handleRequest($request) && $form->isSubmitted() && $form->isValid()) {
            $configuration->setSenderAddressDetails($form->getData());
            $this->addFlash('success', $this->getTranslator()->trans('Successful update.', [], 'Admin.Notifications.Success'));

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

        return $this->render('@Modules/glspoland/views/templates/admin/config/config_form.html.twig', [
            'form' => $form->createView(),
            'can_update' => $canUpdate,
            'layoutTitle' => $this->getTranslator()->trans('Sender details configuration', [], 'Modules.Glspoland.Configuration'),
            'headerTabContent' => $this->renderNav('sender_details'),
            'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
        ]);
    }

    /**
     * @Route(path="/orders", name="orders", methods={"GET", "POST"})
     */
    public function ordersConfig(Request $request, Orders\PersistentConfigurationInterface $configuration): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::READ, self::TAB_NAME);

        $form = $this->createForm(OrdersConfigurationType::class, $configuration->asNonPersistent(), [
            'disabled' => !$canUpdate = $this->isGranted(PageVoter::UPDATE, self::TAB_NAME),
        ]);

        if ($canUpdate && $form->handleRequest($request) && $form->isSubmitted() && $form->isValid()) {
            $configuration->save($form->getData());
            $this->addFlash('success', $this->getTranslator()->trans('Successful update.', [], 'Admin.Notifications.Success'));

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

        return $this->render('@Modules/glspoland/views/templates/admin/config/config_form.html.twig', [
            'form' => $form->createView(),
            'can_update' => $canUpdate,
            'layoutTitle' => $this->getTranslator()->trans('Orders configuration', [], 'Modules.Glspoland.Configuration'),
            'headerTabContent' => $this->renderNav('orders'),
            'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
        ]);
    }

    /**
     * @Route(path="/download/{name}", name="pdf_download", methods={"GET"}, requirements={"name"="[a-zA-Z_]+"})
     */
    public function downloadPdf(string $name, \Module $module): BinaryFileResponse
    {
        $this->denyAccessUnlessGranted(PageVoter::READ, self::TAB_NAME);

        try {
            $file = new File(sprintf('%s/docs/%s.pdf', rtrim($module->getLocalPath(), '/'), $name));
        } catch (FileNotFoundException $e) {
            throw new NotFoundHttpException('File does not exist', $e);
        }

        $response = $this->file($file);
        $response->headers->set('Content-Type', 'application/pdf');

        return $response;
    }

    /**
     * @Route(path="/carriers", name="carriers_index", methods={"GET"})
     */
    public function carriers(CarrierFilters $filters, GridFactoryInterface $gridFactory, GridPresenterInterface $gridPresenter): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::READ, self::TAB_NAME);

        $grid = $gridFactory->getGrid($filters);

        return $this->render('@Modules/glspoland/views/templates/admin/config/carriers.html.twig', [
            'grid' => $gridPresenter->present($grid),
            'layoutTitle' => $this->getTranslator()->trans('Carriers', [], 'Modules.Glspoland.Configuration'),
            'headerTabContent' => $this->renderNav('carriers'),
            'layoutHeaderToolbarBtn' => $this->getCarrierToolbarButtons(),
            'carrier_type_form' => $this->createCarrierTypeChoiceForm()->createView(),
        ]);
    }

    /**
     * @Route("/carriers", name="carriers_filter", methods={"POST"})
     */
    public function filterCarriers(Request $request, FilterableGridDefinitionFactoryInterface $gridDefinitionFactory, GridFilterFormFactoryInterface $filterFormFactory): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::READ, self::TAB_NAME);

        $gridDefinition = $gridDefinitionFactory->getDefinition();
        $filtersForm = $filterFormFactory
            ->create($gridDefinition)
            ->handleRequest($request);

        $redirectParams = [];
        if ($filtersForm->isSubmitted()) {
            $redirectParams[$gridDefinitionFactory->getFilterId()] = [
                'filters' => $filtersForm->getData(),
            ];
        }

        return $this->redirectToRoute('admin_gls_poland_config_carriers_index', $redirectParams, Response::HTTP_SEE_OTHER);
    }

    /**
     * @Route(path="/carriers/new/courier", name="carriers_create_courier", methods={"GET","POST"})
     */
    public function createStandardCarrier(Request $request, CommandBusInterface $bus): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::CREATE, self::CARRIERS_TAB_NAME);

        $command = new CreateCourierCarrierCommand($this->getTranslator()->trans('GLS Courier', [], 'Modules.Glspoland.Carrier'), ['PL']);
        $form = $this
            ->createForm(CreateCourierCarrierType::class, $command)
            ->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $bus->handle($form->getData());
            $this->addFlash('success', $this->getTranslator()->trans('Successful creation.', [], 'Admin.Notifications.Success'));

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

        return $this->render('@Modules/glspoland/views/templates/admin/config/courier_carrier_form.html.twig', [
            'form' => $form->createView(),
            'layoutTitle' => $this->getTranslator()->trans('New courier carrier', [], 'Modules.Glspoland.Configuration'),
            'headerTabContent' => $this->renderNav('carriers'),
            'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
        ]);
    }

    /**
     * @Route(path="/carriers/new/sds", name="carriers_create_sds", methods={"GET","POST"})
     */
    public function createShopDeliveryCarrier(Request $request, CommandBusInterface $bus): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::CREATE, self::CARRIERS_TAB_NAME);

        $command = new CreateShopDeliveryCarrierCommand($this->getTranslator()->trans('GLS ParcelShop', [], 'Modules.Glspoland.Carrier'), ['PL']);
        $form = $this
            ->createForm(CreateShopDeliveryCarrierType::class, $command)
            ->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $bus->handle($form->getData());
            $this->addFlash('success', $this->getTranslator()->trans('Successful creation.', [], 'Admin.Notifications.Success'));

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

        return $this->render('@Modules/glspoland/views/templates/admin/config/carrier_form.html.twig', [
            'form' => $form->createView(),
            'layoutTitle' => $this->getTranslator()->trans('New parcel shop delivery carrier', [], 'Modules.Glspoland.Configuration'),
            'headerTabContent' => $this->renderNav('carriers'),
            'layoutHeaderToolbarBtn' => $this->getToolbarButtons(),
        ]);
    }

    /**
     * @Route("/carriers/{id}/delete", name="carriers_delete", methods={"POST"}, requirements={"id"="\d+"})
     */
    public function deleteCarrier(int $id, CommandBusInterface $bus): Response
    {
        $this->denyAccessUnlessGranted(PageVoter::DELETE, self::TAB_NAME);

        $bus->handle(new DeleteCarrierCommand($id));
        $this->addFlash('success', $this->getTranslator()->trans('Successful deletion', [], 'Admin.Notifications.Success'));

        return $this->redirectToRoute('admin_gls_poland_config_carriers_index', [], Response::HTTP_SEE_OTHER);
    }

    private function getCarrierToolbarButtons(): array
    {
        if (!$this->isGranted(PageVoter::CREATE, self::CARRIERS_TAB_NAME)) {
            return $this->getToolbarButtons();
        }

        return array_merge([
            'new_carrier' => [
                'modal_target' => '#new_carrier_modal',
                'href' => $this->generateUrl('admin_gls_poland_config_carriers_create_courier'),
                'desc' => $this->getTranslator()->trans('New carrier', [], 'Modules.Glspoland.Configuration'),
                'icon' => 'add_circle_outline',
            ],
        ], $this->getToolbarButtons());
    }

    private function getToolbarButtons(): array
    {
        return [
            'docs' => [
                'href' => $this->generateUrl('admin_gls_poland_config_pdf_download', [
                    'name' => 'documentation',
                ]),
                'desc' => $this->getTranslator()->trans('Download documentation', [], 'Modules.Glspoland.Configuration'),
                'icon' => 'article',
            ],
            'user_guidelines' => [
                'href' => $this->generateUrl('admin_gls_poland_config_pdf_download', [
                    'name' => 'user_guidelines',
                ]),
                'desc' => $this->getTranslator()->trans('Download user guidelines', [], 'Modules.Glspoland.Configuration'),
                'icon' => 'article',
            ],
        ];
    }

    private function renderNav(string $activeTab): string
    {
        return $this->renderView('@Modules/glspoland/views/templates/admin/config/nav.html.twig', [
            'nav_items' => [
                'api' => [
                    'url' => $this->generateUrl('admin_gls_poland_config_api'),
                    'label' => $this->getTranslator()->trans('API', [], 'Modules.Glspoland.Configuration'),
                    'active' => 'api' === $activeTab,
                ],
                'sender_details' => [
                    'url' => $this->generateUrl('admin_gls_poland_config_sender_details'),
                    'label' => $this->getTranslator()->trans('Sender details', [], 'Modules.Glspoland.Configuration'),
                    'active' => 'sender_details' === $activeTab,
                ],
                'carriers' => [
                    'url' => $this->generateUrl('admin_gls_poland_config_carriers_index'),
                    'label' => $this->getTranslator()->trans('Carriers', [], 'Modules.Glspoland.Configuration'),
                    'active' => 'carriers' === $activeTab,
                ],
                'orders' => [
                    'url' => $this->generateUrl('admin_gls_poland_config_orders'),
                    'label' => $this->getTranslator()->trans('Orders', [], 'Modules.Glspoland.Configuration'),
                    'active' => 'orders' === $activeTab,
                ],
            ],
        ]);
    }

    private function createCarrierTypeChoiceForm(): FormInterface
    {
        return $this->createForm(CarrierTypeChoiceType::class, null, [
            'choice_attr' => function ($choice, $key, $value): array {
                $url = CarrierType::SHOP_DELIVERY === $value
                    ? $this->generateUrl('admin_gls_poland_config_carriers_create_sds')
                    : $this->generateUrl('admin_gls_poland_config_carriers_create_courier');

                return ['data-url' => $url];
            },
            'label' => $this->getTranslator()->trans('Carrier type', [], 'Modules.Glspoland.Carrier'),
        ]);
    }
}
