<?php

declare(strict_types=1);

namespace Macopedia\OrlenShipping\Controller\Adminhtml\Shipment;

use Macopedia\OrlenShipping\Model\Api\OrlenApiException;
use Magento\Framework\Filesystem\Driver\File;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Magento\Sales\Controller\Adminhtml\Order\AbstractMassAction;
use Magento\Sales\Model\Order\Shipment;
use Magento\Ui\Component\MassAction\Filter;
use Magento\Backend\App\Action\Context;
use Magento\Shipping\Model\Shipping\LabelGenerator;
use Magento\Framework\App\Response\Http\FileFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory;
use Macopedia\OrlenShipping\Model\ResourceModel\Protocol as ProtocolResource;
use Macopedia\OrlenShipping\Model\ProtocolFactory;
use Macopedia\OrlenShipping\Helper\Data;
use Macopedia\OrlenShipping\Model\Carrier\OrlenShipping;
use Macopedia\OrlenShipping\Model\Api\GenerateProtocol;

class MassGenerateProtocol extends AbstractMassAction
{
    /**
     * Authorization level of a basic admin session
     *
     * @see _isAllowed()
     */
    public const ADMIN_RESOURCE = 'Magento_Sales::shipment';

    /**
     * @var LabelGenerator
     */
    protected $labelGenerator;

    /**
     * @var FileFactory
     */
    protected $fileFactory;

    /**
     * @param Context $context
     * @param Filter $filter
     * @param FileFactory $fileFactory
     * @param LabelGenerator $labelGenerator
     * @param CollectionFactory $collectionFactory
     * @param GenerateProtocol $generateProtocol
     * @param ProtocolFactory $protocolFactory
     * @param ProtocolResource $protocolResource
     * @param Data $helper
     * @param File $fileDriver
     */
    public function __construct(
        Context $context,
        Filter $filter,
        FileFactory $fileFactory,
        LabelGenerator $labelGenerator,
        CollectionFactory $collectionFactory,
        private readonly GenerateProtocol $generateProtocol,
        private readonly ProtocolFactory $protocolFactory,
        private readonly ProtocolResource $protocolResource,
        private readonly Data $helper,
        private readonly File $fileDriver
    ) {
        $this->collectionFactory = $collectionFactory;
        $this->fileFactory = $fileFactory;
        $this->labelGenerator = $labelGenerator;
        parent::__construct($context, $filter);
    }

    /**
     * Batch generate shipping protocols
     *
     * @param AbstractCollection $collection
     * @return ResultInterface
     * @throws OrlenApiException
     */
    protected function massAction(AbstractCollection $collection)
    {
        $selectedCount = $collection->getSize();
        if ($selectedCount > Data::MAX_ALLOWED_SHIPMENTS_IN_PROTOCOL) {
            $this->messageManager->addErrorMessage(
                __('You can select a maximum of %1 shipments.', Data::MAX_ALLOWED_SHIPMENTS_IN_PROTOCOL)
            );
            return $this->resultRedirectFactory->create()->setPath('sales/shipment/');
        }

        try {
            $trackNumbers = [];
            $emptyTracks = [];
            $notOrlenShippingNumbers = [];
            if ($selectedCount) {
                /** @var Shipment $shipment */
                foreach ($collection as $shipment) {
                    if (!str_contains($shipment->getOrder()->getShippingMethod(), OrlenShipping::CODE)) {
                        $notOrlenShippingNumbers[] = $shipment->getIncrementId();
                        continue;
                    }
                    $allTracks = $shipment->getAllTracks();
                    if (count($allTracks) > 0) {
                        foreach ($allTracks as $track) {
                            $trackNumbers[] = $track->getTrackNumber();
                        }
                    } else {
                        $emptyTracks[] = $shipment->getIncrementId();
                    }
                }
            }

            if (!empty($notOrlenShippingNumbers)) {
                $this->messageManager->addError(
                    __('There are no ORLEN shipments corresponding to the following numbers: ')
                    . implode(', ', $notOrlenShippingNumbers)
                );
            }

            if (!empty($emptyTracks)) {
                $this->messageManager->addError(
                    __('Some shipment numbers are missing shipping labels: ')
                    . implode(', ', $emptyTracks)
                );
            }

            if (!empty($trackNumbers)) {
                $protocolResponse = $this->generateProtocol->generateProtocol($trackNumbers);
                $fileRawPdf = $protocolResponse->LabelData;
                $protocolCode = $protocolResponse->ProtocolCode;
                $fileName = 'protocol_' . $protocolCode . '.pdf';
                $filePath = $this->helper->getShippingProtocolFolderPath() . $fileName;

                $directory = $this->fileDriver->getParentDirectory($filePath);
                if (!$this->fileDriver->isDirectory($directory)) {
                    $this->fileDriver->createDirectory($directory, 0775);
                }

                $this->fileDriver->filePutContents($filePath, $fileRawPdf);
                $protocol = $this->protocolFactory->create();
                $protocol->setData('file_name', $fileName);
                $protocol->setData('code', $protocolCode);
                $this->protocolResource->save($protocol);

                foreach ($collection as $shipment) {
                    if (!str_contains($shipment->getOrder()->getShippingMethod(), OrlenShipping::CODE)) {
                        continue;
                    }
                    if (!empty($shipment->getAllTracks())) {
                        $shipment->setData('protocol_code', $protocolCode);
                        $shipment->save();
                    }
                }

                $this->messageManager->addSuccessMessage(__('Protocol has been created successfully.'));
            }
        } catch (\Exception $e) {
            $this->messageManager->addErrorMessage(__('Failed to save protocol: ') . $e->getMessage());
        }

        return $this->resultRedirectFactory->create()->setPath('sales/shipment/');
    }
}
