<?php

declare(strict_types=1);

namespace Gls\GlsPoland\MessageHandler\Consignment;

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 (\Throwable $fault) {
                $errors[$orderId] = $fault;
            }
        }

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

    private function filterOrderIds(BulkCreateConsignmentsCommand $command): array
    {
        $orderIds = array_values(array_unique(array_filter($command->getOrderIds(), static function (int $orderId): bool {
            return 0 < $orderId;
        })));

        if ([] === $orderIds) {
            return [];
        }

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

        if ([] === $orderIds) {
            return [];
        }

        $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);
    }
}