<?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\Configuration\Repository;

use InPost\International\Api\Shipment\Model\Sender;
use InPost\International\Api\Shipment\Model\Service\Insurance;
use InPost\International\Configuration\Enum\ShipmentReference;
use InPost\International\Configuration\ShippingConfiguration;
use InPost\International\Serializer\SafeDeserializerTrait;
use InPost\International\Shipment\ShippingMethod;
use PrestaShop\PrestaShop\Core\Domain\Configuration\ShopConfigurationInterface;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use Symfony\Component\Serializer\SerializerInterface;

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

final class ShippingConfigurationRepository implements ShippingConfigurationRepositoryInterface
{
    use SafeDeserializerTrait;

    private const SENDER_KEY = 'INPOST_INTL_SHIPPING_SENDER';
    private const DEFAULT_ORIGIN_KEY = 'INPOST_INTL_SHIPPING_DEFAULT_ORIGIN';
    private const DEFAULT_REFERENCE_KEY = 'INPOST_INTL_SHIPPING_DEFAULT_REFERENCE';
    private const DEFAULT_PARCEL_COMMENT_KEY = 'INPOST_INTL_SHIPPING_DEFAULT_PARCEL_COMMENT';
    private const DEFAULT_INSURANCE_KEY = 'INPOST_INTL_SHIPPING_DEFAULT_INSURANCE';

    /**
     * @var ShopConfigurationInterface
     */
    private $configuration;

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

    public function __construct(ShopConfigurationInterface $configuration, SerializerInterface $serializer)
    {
        $this->configuration = $configuration;
        $this->serializer = $serializer;
    }

    public function getShippingConfiguration(): ?ShippingConfiguration
    {
        if (array_key_exists('shipping', $this->cache)) {
            return $this->cache['shipping'];
        }

        if (null === $sender = $this->getSender()) {
            return $this->cache['shipping'] = null;
        }

        return $this->cache['shipping'] = new ShippingConfiguration(
            $sender,
            $this->getDefaultOrigin(),
            $this->getDefaultReference(),
            $this->getDefaultParcelComment(),
            $this->getDefaultInsurance()
        );
    }

    public function saveShippingConfiguration(ShippingConfiguration $configuration, ShopConstraint $shops = null): void
    {
        $this->setSender($configuration->getSender(), $shops);
        $this->setDefaultOrigin($configuration->getDefaultShippingMethod(), $shops);
        $this->setDefaultReference($configuration->getDefaultShipmentReference(), $shops);
        $this->setDefaultParcelComment($configuration->getDefaultParcelComment(), $shops);
        $this->setDefaultInsurance($configuration->getDefaultInsurance(), $shops);

        if (null === $shops) {
            $this->cache['shipping'] = $configuration;
        } else {
            unset($this->cache['shipping']);
        }
    }

    private function getSender(): ?Sender
    {
        $value = $this->configuration->get(self::SENDER_KEY);

        if (null === $value) {
            return null;
        }

        return $this->deserialize($value, Sender::class);
    }

    private function setSender(Sender $sender, ?ShopConstraint $shops): void
    {
        $value = $this->serializer->serialize($sender, 'json');
        $this->configuration->set(self::SENDER_KEY, $value, $shops);
    }

    private function getDefaultOrigin(): ShippingMethod
    {
        $value = (string) $this->configuration->get(self::DEFAULT_ORIGIN_KEY);

        return ShippingMethod::tryFrom($value) ?? ShippingMethod::FromPoint();
    }

    private function setDefaultOrigin(ShippingMethod $origin, ?ShopConstraint $shops): void
    {
        $this->configuration->set(self::DEFAULT_ORIGIN_KEY, $origin->value, $shops);
    }

    private function getDefaultReference(): ShipmentReference
    {
        $value = (string) $this->configuration->get(self::DEFAULT_REFERENCE_KEY);

        return ShipmentReference::tryFrom($value) ?? ShipmentReference::OrderNumber();
    }

    private function setDefaultReference(ShipmentReference $reference, ?ShopConstraint $shops): void
    {
        $this->configuration->set(self::DEFAULT_REFERENCE_KEY, $reference->value, $shops);
    }

    private function getDefaultParcelComment(): ?ShipmentReference
    {
        $value = (string) $this->configuration->get(self::DEFAULT_PARCEL_COMMENT_KEY);

        return ShipmentReference::tryFrom($value);
    }

    private function setDefaultParcelComment(?ShipmentReference $reference, ?ShopConstraint $shops): void
    {
        $value = null === $reference ? null : $reference->value;
        $this->configuration->set(self::DEFAULT_PARCEL_COMMENT_KEY, $value, $shops);
    }

    public function getDefaultInsurance(): ?Insurance
    {
        $value = $this->configuration->get(self::DEFAULT_INSURANCE_KEY);

        if (null === $value) {
            return null;
        }

        return $this->deserialize($value, Insurance::class);
    }

    public function setDefaultInsurance(?Insurance $insurance, ShopConstraint $shops = null): void
    {
        $value = null === $insurance ? null : $this->serializer->serialize($insurance, 'json');
        $this->configuration->set(self::DEFAULT_INSURANCE_KEY, $value, $shops);
    }
}
