<?php

declare(strict_types=1);

namespace Gls\GlsPoland\MessageHandler\Consignment;

use Gls\GlsPoland\AdePlus\Fault\AdePlusFault;
use Gls\GlsPoland\Entity\Consignment;
use Gls\GlsPoland\Message\BulkCreateConsignmentsCommand;
use Gls\GlsPoland\PrestaShop\ObjectModel\Repository\OrderRepository;
use Gls\GlsPoland\PrestaShop\Order\CreateConsignmentCommandFactoryInterface;
use Gls\GlsPoland\Repository\ConsignmentRepository;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;

final class BulkCreateConsignmentsHandler implements BulkCreateConsignmentsHandlerInterface
{
    private $bus;
    private $repository;
    private $commandFactory;
    private $orderRepository;

    public function __construct(CommandBusInterface $bus, ConsignmentRepository $repository, OrderRepository $orderRepository, CreateConsignmentCommandFactoryInterface $commandFactory)
    {
        $this->bus = $bus;
        $this->repository = $repository;
        $this->orderRepository = $orderRepository;
        $this->commandFactory = $commandFactory;
    }

    public function handle(BulkCreateConsignmentsCommand $command): CreateConsignmentsResult
    {
        $consignments = [];
        $errors = [];

        foreach ($this->filterOrderIds($command) as $orderId) {
            try {
                $consignments[$orderId] = $this->createConsignment($orderId);
            } catch (AdePlusFault $fault) {
                $errors[$orderId] = $fault;
            }
        }

        return new CreateConsignmentsResult($consignments, $errors);
    }

    private function filterOrderIds(BulkCreateConsignmentsCommand $command): array
    {
        if ([] === $orderIds = $command->getOrderIds()) {
            return [];
        }

        $existingConsignments = $this->repository->findBy(['orderId' => $orderIds]);
        $orderIds = array_diff($orderIds, array_map(static function (Consignment $consignment): int {
            return $consignment->getOrderId();
        }, $existingConsignments));

        $orders = $this->orderRepository->findByCarrierModuleName('glspoland', ...$orderIds);

        return array_map(static function (\Order $order): int {
            return (int) $order->id;
        }, $orders);
    }

    private function createConsignment(int $orderId): Consignment
    {
        $command = $this->commandFactory->create($orderId);

        return $this->bus->handle($command);
    }
}
