<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Configuration\Orders;

use Gls\GlsPoland\AdePlus\Printout\LabelsPrintMode;
use Gls\GlsPoland\AdePlus\Printout\PickupReceiptPrintMode;
use Gls\GlsPoland\Configuration\ConfigurationInterface as Persistence;
use Gls\GlsPoland\Configuration\SenderAddressConfigurationInterface;
use Gls\GlsPoland\Consignment\DTO\AddressDetails;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\SerializerInterface;

final class PersistentConfiguration implements PersistentConfigurationInterface, SenderAddressConfigurationInterface
{
    private const LABEL_PRINT_MODE = 'GLS_POLAND_LABEL_PRINT_MODE';
    private const PICKUP_RECEIPT_PRINT_MODE = 'GLS_POLAND_PICKUP_RECEIPT_PRINT_MODE';
    private const DOWNLOAD_LABELS_AS_SINGLE_FILE = 'GLS_POLAND_DOWNLOAD_LABELS_AS_SINGLE_FILE';
    private const LABEL_PRINTED_OS_ID = 'GLS_POLAND_LABEL_PRINTED_OS_ID';
    private const CONSIGNMENT_DELETED_OS_ID = 'GLS_POLAND_CONSIGNMENT_DELETED_OS_ID';
    private const DEFAULT_REFERENCES = 'GLS_POLAND_DEFAULT_REFERENCES';
    private const DEFAULT_NOTES = 'GLS_POLAND_DEFAULT_NOTES';
    private const DEFAULT_PARCEL_WEIGHT = 'GLS_POLAND_DEFAULT_PARCEL_WEIGHT';
    private const SENDER_ADDRESS_DETAILS = 'GLS_POLAND_SENDER_ADDRESS_DETAILS';

    private $configuration;

    private $senderDetails;
    private $serializer;

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

    public function getLabelsPrintMode(): LabelsPrintMode
    {
        $value = (string) $this->configuration->get(self::LABEL_PRINT_MODE);

        return LabelsPrintMode::tryFrom($value) ?? LabelsPrintMode::from(current(LabelsPrintMode::PRINT_MODES));
    }

    public function setLabelsPrintMode(LabelsPrintMode $printMode): void
    {
        $this->configuration->set(self::LABEL_PRINT_MODE, (string) $printMode);
    }

    public function getPickupReceiptPrintMode(): PickupReceiptPrintMode
    {
        $value = (string) $this->configuration->get(self::PICKUP_RECEIPT_PRINT_MODE);

        return PickupReceiptPrintMode::tryFrom($value) ?? PickupReceiptPrintMode::from(current(PickupReceiptPrintMode::PRINT_MODES));
    }

    public function setPickupReceiptPrintMode(PickupReceiptPrintMode $printMode): void
    {
        $this->configuration->set(self::PICKUP_RECEIPT_PRINT_MODE, (string) $printMode);
    }

    public function isDownloadLabelsAsSingleFile(): bool
    {
        return (bool) $this->configuration->get(self::DOWNLOAD_LABELS_AS_SINGLE_FILE);
    }

    public function setDownloadLabelsAsSingleFile(bool $downloadAsSingleFile): void
    {
        $this->configuration->set(self::DOWNLOAD_LABELS_AS_SINGLE_FILE, $downloadAsSingleFile);
    }

    public function getLabelPrintedOrderStatusId(): ?int
    {
        return (int) $this->configuration->get(self::LABEL_PRINTED_OS_ID) ?: null;
    }

    public function setLabelPrintedOrderStatusId(?int $statusId): void
    {
        $this->configuration->set(self::LABEL_PRINTED_OS_ID, $statusId);
    }

    public function getConsignmentDeletedOrderStatusId(): ?int
    {
        return (int) $this->configuration->get(self::CONSIGNMENT_DELETED_OS_ID) ?: null;
    }

    public function setConsignmentDeletedOrderStatusId(?int $statusId): void
    {
        $this->configuration->set(self::CONSIGNMENT_DELETED_OS_ID, $statusId);
    }

    public function getDefaultReferences(): ?string
    {
        return $this->configuration->get(self::DEFAULT_REFERENCES);
    }

    public function setDefaultReferences(?string $references): void
    {
        $this->configuration->set(self::DEFAULT_REFERENCES, $references);
    }

    public function getDefaultNotes(): ?string
    {
        return $this->configuration->get(self::DEFAULT_NOTES);
    }

    public function setDefaultNotes(?string $notes): void
    {
        $this->configuration->set(self::DEFAULT_NOTES, $notes);
    }

    public function getDefaultParcelWeightKg(): float
    {
        $weight = $this->configuration->get(self::DEFAULT_PARCEL_WEIGHT) ?? 0.3;

        return (float) $weight;
    }

    public function setDefaultParcelWeightKg(float $weight): void
    {
        $this->configuration->set(self::DEFAULT_PARCEL_WEIGHT, $weight);
    }

    public function asNonPersistent(): ConfigurationInterface
    {
        return InMemoryConfiguration::from($this);
    }

    public function save(ConfigurationInterface $configuration): void
    {
        $this->setLabelsPrintMode($configuration->getLabelsPrintMode());
        $this->setPickupReceiptPrintMode($configuration->getPickupReceiptPrintMode());
        $this->setDownloadLabelsAsSingleFile($configuration->isDownloadLabelsAsSingleFile());
        $this->setLabelPrintedOrderStatusId($configuration->getLabelPrintedOrderStatusId());
        $this->setConsignmentDeletedOrderStatusId($configuration->getConsignmentDeletedOrderStatusId());
        $this->setDefaultReferences($configuration->getDefaultReferences());
        $this->setDefaultNotes($configuration->getDefaultNotes());
        $this->setDefaultParcelWeightKg($configuration->getDefaultParcelWeightKg());
    }

    public function getSenderAddressDetails(): ?AddressDetails
    {
        if (!isset($this->senderDetails)) {
            $this->senderDetails = $this->loadSenderAddressDetails() ?? false;
        }

        return $this->senderDetails ? clone $this->senderDetails : null;
    }

    public function setSenderAddressDetails(?AddressDetails $address): void
    {
        $this->saveSenderAddressDetails($address);
        $this->senderDetails = $address ? clone $address : false;
    }

    private function loadSenderAddressDetails(): ?AddressDetails
    {
        if (null === $value = $this->configuration->get(self::SENDER_ADDRESS_DETAILS)) {
            return null;
        }

        try {
            return $this->serializer->deserialize($value, AddressDetails::class, JsonEncoder::FORMAT);
        } catch (\Exception $e) {
            return null;
        }
    }

    private function saveSenderAddressDetails(?AddressDetails $senderDetails): void
    {
        $value = $senderDetails
            ? $this->serializer->serialize($senderDetails, JsonEncoder::FORMAT)
            : null;

        $this->configuration->set(self::SENDER_ADDRESS_DETAILS, $value);
    }
}
