<?php

declare(strict_types=1);

namespace Gls\GlsPoland\MessageHandler\Labels;

use Gls\GlsPoland\AdePlus\Printout\LabelsPrintMode;
use Gls\GlsPoland\Configuration\Orders;
use Gls\GlsPoland\Consignment\Exception\ConsignmentNotFoundException;
use Gls\GlsPoland\Entity\Consignment;
use Gls\GlsPoland\EventDispatcher\EventDispatcherInterface;
use Gls\GlsPoland\Message\PrintLabelsCommand;
use Gls\GlsPoland\MessageHandler\Labels\Download\DownloadLabelsHandlerInterface;
use Gls\GlsPoland\Download\Base64EncodedFile;
use Gls\GlsPoland\Download\Base64EncodedFilesArchive;
use Gls\GlsPoland\Download\StreamableFileInterface;
use Gls\GlsPoland\Repository\ConsignmentRepository;

final class PrintLabelsHandler implements PrintLabelsHandlerInterface
{
    private $repository;
    private $configuration;
    private $downloadHandler;
    private $dispatcher;

    public function __construct(ConsignmentRepository $repository, Orders\ConfigurationInterface $configuration, DownloadLabelsHandlerInterface $downloadHandler, EventDispatcherInterface $dispatcher)
    {
        $this->repository = $repository;
        $this->configuration = $configuration;
        $this->downloadHandler = $downloadHandler;
        $this->dispatcher = $dispatcher;
    }

    public function handle(PrintLabelsCommand $command): StreamableFileInterface
    {
        if (null === $consignment = $this->repository->find($command->getConsignmentId())) {
            throw ConsignmentNotFoundException::create($command->getConsignmentId());
        }

        $this->dispatcher->dispatch(new LabelsEvent($consignment), LabelsEvent::PRINT_BEFORE);

        $printMode = $this->configuration->getLabelsPrintMode();
        $labels = $this->configuration->isDownloadLabelsAsSingleFile()
            ? $this->downloadSingleFile($consignment, $printMode)
            : $this->downloadSeparateFiles($consignment, $printMode);

        $this->dispatcher->dispatch(new LabelsEvent($consignment), LabelsEvent::PRINTED);

        return $labels;
    }

    private function downloadSingleFile(Consignment $consignment, LabelsPrintMode $printMode): Base64EncodedFile
    {
        $labels = $this->downloadHandler->downloadSingle($consignment, $printMode);

        return new Base64EncodedFile($labels, $printMode);
    }

    private function downloadSeparateFiles(Consignment $consignment, LabelsPrintMode $printMode): StreamableFileInterface
    {
        $labels = $this->downloadHandler->downloadSeparate($consignment, $printMode);

        if (1 !== count($labels)) {
            return new Base64EncodedFilesArchive($labels, $printMode);
        }

        if ($labels instanceof \Traversable) {
            $labels = iterator_to_array($labels);
        }

        return new Base64EncodedFile(current($labels), $printMode);
    }
}
