<?php

declare(strict_types=1);

namespace Gls\GlsPoland\MessageHandler\Consignment;

use Gls\GlsPoland\AdePlus\Common\Id;
use Gls\GlsPoland\AdePlus\Consignment\Consignment;
use Gls\GlsPoland\AdePlus\Fault\AdePlusFault;
use Gls\GlsPoland\AdePlus\PreparingBox\ClientInterface;
use Gls\GlsPoland\Consignment\DTO;
use Gls\GlsPoland\Entity;
use Gls\GlsPoland\Message\CreateConsignmentCommand;
use Gls\GlsPoland\Repository\ConsignmentRepository;

final class CreateConsignmentHandler implements CreateConsignmentHandlerInterface
{
    private $client;
    private $builderFactory;
    private $repository;

    public function __construct(ClientInterface $client, ConsignmentBuilderFactoryInterface $builderFactory, ConsignmentRepository $repository)
    {
        $this->client = $client;
        $this->builderFactory = $builderFactory;
        $this->repository = $repository;
    }

    public function handle(CreateConsignmentCommand $command): Entity\Consignment
    {
        $consignment = $this->buildConsignment($command);
        $consignmentId = $this->client->addConsignment($consignment);
        $this->assertValidConsignmentId($consignmentId);

        $entity = $this->createEntity($consignmentId, $command);
        $this->repository->add($entity);

        return $entity;
    }

    private function buildConsignment(CreateConsignmentCommand $command): Consignment
    {
        return $this->builderFactory
            ->create($command)
            ->setNotes($command->getNotes())
            ->setReferences($command->getReferences())
            ->setSenderDetails($command->getSenderDetails())
            ->setRecipientDetails($command->getRecipientDetails())
            ->setServiceDetails($command->getServiceDetails())
            ->setShippingDate($command->getShippingDate())
            ->setParcels(...$command->getParcels()->toArray())
            ->build();
    }

    private function assertValidConsignmentId(Id $consignmentId): void
    {
        if (0 < $consignmentId->getId()) {
            return;
        }

        throw new AdePlusFault(
            sprintf('ADE-Plus error (code: err_invalid_consignment_id): "id: %d".', $consignmentId->getId()),
            'err_invalid_consignment_id'
        );
    }

    private function createEntity(Id $consignmentId, CreateConsignmentCommand $command): Entity\Consignment
    {
        $recipientDetails = $command->getRecipientDetails();

        $parcels = array_map(static function (DTO\Parcel $parcel): Entity\Parcel {
            return new Entity\Parcel(
                $parcel->getWeightKg(),
                $parcel->getReference()
            );
        }, $command->getParcels()->toArray());

        return new Entity\Consignment(
            $consignmentId,
            $this->client->isSandbox(),
            $command->getOrderId(),
            $command->getReferences(),
            $recipientDetails->getEmail(),
            $recipientDetails->getPhone(),
            ...$parcels
        );
    }
}
