<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Shipment\Grid;

use InPost\International\Carrier\CarrierType;
use InPost\International\Controller\Admin\PickupOrderController;
use InPost\International\Enum\Form\Type\EnumType;
use InPost\International\PrestaShop\Grid\Action\Bulk\ModalFormSubmitBulkAction as CustomModalFormSubmitBulkAction;
use InPost\International\PrestaShop\Grid\Column\ExternalLinkColumn;
use InPost\International\Shipment\ShippingMethod;
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\BulkActionCollection;
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\BulkActionCollectionInterface;
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\BulkActionInterface;
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\Type\SubmitBulkAction;
use PrestaShop\PrestaShop\Core\Grid\Action\GridActionCollection;
use PrestaShop\PrestaShop\Core\Grid\Action\GridActionCollectionInterface;
use PrestaShop\PrestaShop\Core\Grid\Action\Row\RowActionCollection;
use PrestaShop\PrestaShop\Core\Grid\Action\Row\RowActionCollectionInterface;
use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\LinkRowAction;
use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\SubmitRowAction;
use PrestaShop\PrestaShop\Core\Grid\Action\Type\SimpleGridAction;
use PrestaShop\PrestaShop\Core\Grid\Column\ColumnCollection;
use PrestaShop\PrestaShop\Core\Grid\Column\ColumnCollectionInterface;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ActionColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\BulkActionColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\DataColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\DateTimeColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\IdentifierColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\LinkColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\DataColumn as LegacyDataColumn;
use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\AbstractFilterableGridDefinitionFactory;
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
use PrestaShop\PrestaShop\Core\Grid\Filter\FilterCollection;
use PrestaShop\PrestaShop\Core\Grid\Filter\FilterCollectionInterface;
use PrestaShop\PrestaShop\Core\Hook\HookDispatcherInterface;
use PrestaShopBundle\Event\Dispatcher\NullDispatcher;
use PrestaShopBundle\Form\Admin\Type\DateRangeType;
use PrestaShopBundle\Form\Admin\Type\SearchAndResetType;
use PrestaShopBundle\Form\Admin\Type\YesAndNoChoiceType;
use PrestaShopBundle\Security\Voter\PageVoter;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

if (!defined('_PS_VERSION_')) {
    exit;
}

final class ShipmentGridDefinitionFactory extends AbstractFilterableGridDefinitionFactory
{
    public const GRID_ID = 'inpost_intl_shipment';
    public const BULK_IDS_INPUT_NAME = self::GRID_ID . '_ids';

    /**
     * @var AuthorizationCheckerInterface
     */
    private $authorizationChecker;

    public function __construct(TranslatorInterface $translator, AuthorizationCheckerInterface $authorizationChecker, HookDispatcherInterface $hookDispatcher = null)
    {
        parent::__construct($hookDispatcher ?? new NullDispatcher());
        $this->translator = $translator;
        $this->authorizationChecker = $authorizationChecker;
    }

    protected function getId(): string
    {
        return self::GRID_ID;
    }

    protected function getName(): string
    {
        return $this->trans('Shipments', [], 'Modules.Inpostinternational.Shipment');
    }

    protected function getColumns(): ColumnCollectionInterface
    {
        $dataColumnClass = class_exists(DataColumn::class) ? DataColumn::class : LegacyDataColumn::class;

        return (new ColumnCollection())
            ->add(
                (new BulkActionColumn('ids'))
                    ->setOptions([
                        'bulk_field' => 'id',
                    ])
            )
            ->add(
                (new IdentifierColumn('id'))
                    ->setName($this->trans('ID', [], 'Admin.Global'))
                    ->setOptions([
                        'identifier_field' => 'id',
                    ])
            )
            ->add(
                (new ExternalLinkColumn('tracking_number'))
                    ->setName($this->trans('Shipment number', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'tracking_number',
                        'url_field' => 'tracking_url',
                    ])
            )
            ->add(
                (new LinkColumn('order_reference'))
                    ->setName($this->trans('Order', [], 'Admin.Global'))
                    ->setOptions([
                        'field' => 'order_reference',
                        'route' => 'admin_orders_view',
                        'route_param_name' => 'orderId',
                        'route_param_field' => 'id_order',
                    ])
            )
            ->add(
                (new $dataColumnClass('status'))
                    ->setName($this->trans('Status', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'status',
                    ])
            )
            ->add(
                (new $dataColumnClass('shipping_cost'))
                    ->setName($this->trans('Shipping cost', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'shipping_cost',
                    ])
            )
            ->add(
                (new $dataColumnClass('service'))
                    ->setName($this->trans('Service', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'service',
                        'sortable' => false,
                    ])
            )
            ->add(
                (new $dataColumnClass('parcel_dimensions'))
                    ->setName($this->trans('Parcel dimensions', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'parcel_dimensions',
                        'sortable' => false,
                    ])
            )
            ->add(
                (new $dataColumnClass('parcel_weight'))
                    ->setName($this->trans('Parcel weight', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'parcel_weight',
                        'sortable' => false,
                    ])
            )
            ->add(
                (new $dataColumnClass('shipping_method'))
                    ->setName($this->trans('Shipping method', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'shipping_method',
                        'sortable' => false,
                    ])
            )
            ->add(
                (new DateTimeColumn('order_date'))
                    ->setName($this->trans('Order date', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'order_date',
                    ])
            )
            ->add(
                (new DateTimeColumn('created_at'))
                    ->setName($this->trans('Created at', [], 'Modules.Inpostinternational.Admin'))
                    ->setOptions([
                        'field' => 'created_at',
                    ])
            )
            ->add(
                (new $dataColumnClass('recipient_email'))
                    ->setName($this->trans('Email', [], 'Admin.Global'))
                    ->setOptions([
                        'field' => 'recipient_email',
                    ])
            )
            ->add(
                (new $dataColumnClass('recipient_phone'))
                    ->setName($this->trans('Phone', [], 'Admin.Global'))
                    ->setOptions([
                        'field' => 'recipient_phone',
                    ])
            )
            ->add(
                (new $dataColumnClass('parcel_comment'))
                    ->setName($this->trans('Customer reference', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'parcel_comment',
                    ])
            )
            ->add(
                (new $dataColumnClass('has_pickup_order'))
                    ->setName($this->trans('Pickup order', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'field' => 'has_pickup_order',
                    ])
            )
            ->add(
                (new ActionColumn('actions'))
                    ->setName($this->trans('Actions', [], 'Admin.Global'))
                    ->setOptions([
                        'actions' => $this->getRowActions(),
                    ])
            );
    }

    protected function getFilters(): FilterCollectionInterface
    {
        return (new FilterCollection())
            ->add(
                (new Filter('id', NumberType::class))
                    ->setTypeOptions([
                        'attr' => [
                            'placeholder' => $this->trans('Search ID', [], 'Admin.Actions'),
                        ],
                        'required' => false,
                    ])
                    ->setAssociatedColumn('id')
            )
            ->add(
                (new Filter('tracking_number', TextType::class))
                    ->setTypeOptions([
                        'attr' => [
                            'placeholder' => $this->trans('Search shipment number', [], 'Modules.Inpostinternational.Shipment'),
                        ],
                        'required' => false,
                    ])
                    ->setAssociatedColumn('tracking_number')
            )
            ->add(
                (new Filter('order_reference', TextType::class))
                    ->setTypeOptions([
                        'attr' => [
                            'placeholder' => $this->trans('Search order', [], 'Modules.Inpostinternational.Shipment'),
                        ],
                        'required' => false,
                    ])
                    ->setAssociatedColumn('order_reference')
            )
            ->add(
                (new Filter('service', EnumType::class))
                    ->setTypeOptions([
                        'class' => CarrierType::class,
                        'input' => 'value',
                        'placeholder' => $this->trans('All', [], 'Admin.Global'),
                        'required' => false,
                    ])
                    ->setAssociatedColumn('service')
            )
            ->add(
                (new Filter('shipping_method', EnumType::class))
                    ->setTypeOptions([
                        'class' => ShippingMethod::class,
                        'input' => 'value',
                        'placeholder' => $this->trans('All', [], 'Admin.Global'),
                        'required' => false,
                    ])
                    ->setAssociatedColumn('shipping_method')
            )
            ->add(
                (new Filter('order_date', DateRangeType::class))
                    ->setTypeOptions([
                        'required' => false,
                    ])
                    ->setAssociatedColumn('order_date')
            )
            ->add(
                (new Filter('created_at', DateRangeType::class))
                    ->setTypeOptions([
                        'required' => false,
                    ])
                    ->setAssociatedColumn('created_at')
            )
            ->add(
                (new Filter('recipient_email', TextType::class))
                    ->setTypeOptions([
                        'attr' => [
                            'placeholder' => $this->trans('Search email', [], 'Admin.Actions'),
                        ],
                        'required' => false,
                    ])
                    ->setAssociatedColumn('recipient_email')
            )
            ->add(
                (new Filter('recipient_phone', TextType::class))
                    ->setTypeOptions([
                        'attr' => [
                            'placeholder' => $this->trans('Search phone', [], 'Modules.Inpostinternational.Shipment'),
                        ],
                        'required' => false,
                    ])
                    ->setAssociatedColumn('recipient_phone')
            )
            ->add(
                (new Filter('parcel_comment', TextType::class))
                    ->setTypeOptions([
                        'attr' => [
                            'placeholder' => $this->trans('Search customer reference', [], 'Modules.Inpostinternational.Shipment'),
                        ],
                        'required' => false,
                    ])
                    ->setAssociatedColumn('parcel_comment')
            )
            ->add(
                (new Filter('has_pickup_order', YesAndNoChoiceType::class))
                    ->setTypeOptions([
                        'required' => false,
                    ])
                    ->setAssociatedColumn('has_pickup_order')
            )
            ->add(
                (new Filter('actions', SearchAndResetType::class))
                    ->setTypeOptions([
                        'reset_route' => 'admin_common_reset_search_by_filter_id',
                        'reset_route_params' => [
                            'filterId' => self::GRID_ID,
                        ],
                        'redirect_route' => 'admin_inpost_intl_shipments_index',
                    ])
                    ->setAssociatedColumn('actions')
            );
    }

    protected function getGridActions(): GridActionCollectionInterface
    {
        return (new GridActionCollection())
            ->add(
                (new SimpleGridAction('common_refresh_list'))
                    ->setName($this->trans('Refresh list', [], 'Admin.Advparameters.Feature'))
                    ->setIcon('refresh')
            )
            ->add(
                (new SimpleGridAction('common_show_query'))
                    ->setName($this->trans('Show SQL query', [], 'Admin.Actions'))
                    ->setIcon('code')
            )
            ->add(
                (new SimpleGridAction('common_export_sql_manager'))
                    ->setName($this->trans('Export to SQL Manager', [], 'Admin.Actions'))
                    ->setIcon('storage')
            );
    }

    protected function getBulkActions(): BulkActionCollectionInterface
    {
        $collection = new BulkActionCollection();

        foreach ($this->doGetBulkActions() as $action) {
            $collection->add($action);
        }

        return $collection;
    }

    private function getRowActions(): RowActionCollectionInterface
    {
        return (new RowActionCollection())
            ->add(
                (new LinkRowAction('print_label'))
                    ->setName($this->trans('Print label', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'route' => 'admin_inpost_intl_shipments_print_label',
                        'route_param_name' => 'id',
                        'route_param_field' => 'id',
                    ])
                    ->setIcon('print')
            )
            ->add(
                (new SubmitRowAction('update_status'))
                    ->setName($this->trans('Update status', [], 'Modules.Inpostinternational.Shipment'))
                    ->setOptions([
                        'route' => 'admin_inpost_intl_shipments_update_status',
                        'route_param_name' => 'id',
                        'route_param_field' => 'id',
                    ])
                    ->setIcon('refresh')
            );
    }

    /**
     * @return iterable<BulkActionInterface>
     */
    private function doGetBulkActions(): iterable
    {
        yield $this->createBulkAction(SubmitBulkAction::class, 'print_labels', $this->trans('Print labels', [], 'Modules.Inpostinternational.Shipment'), [
            'submit_route' => 'admin_inpost_intl_shipments_bulk_print_labels',
        ], 'print');

        if ($this->authorizationChecker->isGranted(PageVoter::CREATE, PickupOrderController::TAB_NAME)) {
            yield $this->createBulkAction(CustomModalFormSubmitBulkAction::class, 'create_pickup_order', $this->trans('Create pickup order', [], 'Modules.Inpostinternational.Shipment'), [
                'modal_id' => 'inpost_intl_create_pickup_order_modal',
                'submit_route' => 'admin_inpost_intl_pickup_orders_create',
                'class' => 'js-inpost-intl-create-pickup-order-btn',
            ], 'local_shipping');
        }

        yield $this->createBulkAction(SubmitBulkAction::class, 'update_status', $this->trans('Update statuses', [], 'Modules.Inpostinternational.Shipment'), [
            'submit_route' => 'admin_inpost_intl_shipments_bulk_update_status',
        ], 'refresh');
    }

    /**
     * @template T of BulkActionInterface
     *
     * @param class-string<T> $class
     *
     * @return T
     */
    private function createBulkAction(string $class, string $id, string $name, array $options, string $icon): BulkActionInterface
    {
        $action = (new $class($id))
            ->setName($name)
            ->setOptions($options);

        // added in PS 8
        if (is_callable([$action, 'setIcon'])) {
            $action->setIcon($icon);
        }

        return $action;
    }
}
