<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Shipment\Order;

use Doctrine\ORM\EntityRepository;
use InPost\International\Api\Shipment\Model\Parcel\WeightUnit;
use InPost\International\Configuration\Enum\ShipmentReference;
use InPost\International\Configuration\Repository\ShippingConfigurationRepositoryInterface;
use InPost\International\Entity\ParcelTemplate;
use InPost\International\ParcelTemplate\ParcelTemplateRepositoryInterface;
use InPost\International\Shipment\DTO\Dimensions;
use InPost\International\Shipment\DTO\Parcel;
use InPost\International\Shipment\DTO\Weight;
use InPost\International\Shipment\Message\CreateShipmentCommand;
use PrestaShopBundle\Entity\Lang;
use Symfony\Contracts\Translation\TranslatorInterface;

if (!defined('_PS_VERSION_')) {
    exit;
}

final class ParcelBuilder implements CreateShipmentCommandBuilderInterface
{
    /**
     * @var ShippingConfigurationRepositoryInterface
     */
    private $configuration;

    /**
     * @var ParcelTemplateRepositoryInterface
     */
    private $templateRepository;

    /**
     * @var EntityRepository<Lang>
     */
    private $languageRepository;

    /**
     * @var TranslatorInterface
     */
    private $translator;

    /**
     * @var array{template?: ParcelTemplate|null}
     */
    private $cache = [];

    /**
     * @param EntityRepository<Lang> $languageRepository
     */
    public function __construct(ShippingConfigurationRepositoryInterface $configuration, ParcelTemplateRepositoryInterface $templateRepository, EntityRepository $languageRepository, TranslatorInterface $translator)
    {
        $this->configuration = $configuration;
        $this->templateRepository = $templateRepository;
        $this->languageRepository = $languageRepository;
        $this->translator = $translator;
    }

    public function buildCommand(CreateShipmentCommand $command, \Order $order): void
    {
        $template = $this->getDefaultParcelTemplate();

        if (null !== $template) {
            $parcel = $this->createFromTemplate($template);
        } else {
            $parcel = new Parcel();
        }

        $this->setOrderWeight($parcel, $order);
        $parcel->setComment($this->getComment($order));

        $command->setParcel($parcel);
    }

    private function getDefaultParcelTemplate(): ?ParcelTemplate
    {
        if (array_key_exists('template', $this->cache)) {
            return $this->cache['template'];
        }

        return $this->cache['template'] = $this->templateRepository->getDefault();
    }

    private function createFromTemplate(ParcelTemplate $template): Parcel
    {
        $dimensions = (new Dimensions())
            ->setLength($template->getDimensions()->getLength())
            ->setWidth($template->getDimensions()->getWidth())
            ->setHeight($template->getDimensions()->getHeight())
            ->setUnit($template->getDimensions()->getUnit());

        $weight = (new Weight())
            ->setAmount($template->getWeight()->getWeight())
            ->setUnit($template->getWeight()->getUnit());

        return (new Parcel())
            ->setType($template->getType())
            ->setDimensions($dimensions)
            ->setWeight($weight);
    }

    private function setOrderWeight(Parcel $parcel, \Order $order): void
    {
        if (0. >= $weightKg = (float) $order->getTotalWeight()) {
            return;
        }

        $weight = (new Weight())
            ->setAmount($weightKg)
            ->setUnit(WeightUnit::Kilogram());

        $parcel->setWeight($weight);
    }

    private function getComment(\Order $order): ?string
    {
        if (null === $orderNumber = $this->getOrderNumber($order)) {
            return null;
        }

        return $this->translator->trans('Order no. #{orderNumber}', [
            '{orderNumber}' => $orderNumber,
        ], 'Modules.Inpostinternational.Shipment', $this->getLocale($order));
    }

    private function getOrderNumber(\Order $order): ?string
    {
        if (null === $configuration = $this->configuration->getShippingConfiguration()) {
            return null;
        }

        switch ($configuration->getDefaultParcelComment()) {
            case ShipmentReference::OrderNumber():
                return $order->reference;
            case ShipmentReference::OrderIdentifier():
                return (string) $order->id;
            default:
                return null;
        }
    }

    private function getLocale(\Order $order): ?string
    {
        if (null === $language = $this->languageRepository->find((int) $order->id_lang)) {
            return null;
        }

        return $language->getLocale();
    }
}
