<?php

declare(strict_types=1);

namespace Macopedia\OrlenShipping\Helper;

use Magento\Checkout\Model\Session;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Module\ModuleListInterface;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\FileSystemException;

class Data extends AbstractHelper
{
    public const XML_PATH_MAP_WIDGET_TOKEN = 'carriers/orlenpaczka/map_widget_token';
    public const XML_PATH_ORLEN_ACTIVE = 'carriers/orlenpaczka/active';
    public const XML_PATH_ORLEN_PARTNER_ID = 'carriers/orlenpaczka/partner_id';
    public const XML_PATH_ORLEN_PARTNER_KEY = 'carriers/orlenpaczka/partner_key';
    public const XML_PATH_ORLEN_IS_TEST = 'carriers/orlenpaczka/test';
    public const XML_PATH_ORLEN_API_URL_TEST = 'carriers/orlenpaczka/api_url_test';
    public const XML_PATH_ORLEN_API_URL = 'carriers/orlenpaczka/api_url';
    public const XML_PATH_TRACKING_URL = 'carriers/orlenpaczka/tracking_url';
    public const XML_PATH_DEBUG_MODE = 'carriers/orlenpaczka/debug';
    public const XML_PATH_DEFAULT_BOX_SIZE = 'carriers/orlenpaczka/default_box_size';
    public const XML_PATH_ADDRESS_POSTCODE = 'carriers/orlenpaczka/address/postcode';
    public const XML_PATH_ADDRESS_CITY = 'carriers/orlenpaczka/address/city';
    public const XML_PATH_ADDRESS_STREET = 'carriers/orlenpaczka/address/street';
    public const XML_PATH_ADDRESS_STREET_NUMBER = 'carriers/orlenpaczka/address/street_number';
    public const XML_PATH_ADDRESS_FLAT_NUMBER = 'carriers/orlenpaczka/address/flat_number';
    public const XML_PATH_ADDRESS_PARTNER_NAME = 'carriers/orlenpaczka/address/partner_name';
    public const XML_PATH_ADDRESS_EMAIL = 'carriers/orlenpaczka/address/email';
    public const XML_PATH_ADDRESS_PHONE_NUMBER = 'carriers/orlenpaczka/address/phone_number';
    public const ORLEN_PACK_MAX_WEIGHT = 20;
    public const MAX_ALLOWED_SHIPMENTS_IN_PROTOCOL = 571;
    private const SHIPPING_PROTOCOL_FOLDER = '/orlen_paczka/protocols/';

    /**
     * @param Context $context
     * @param Session $session
     * @param ModuleListInterface $moduleList
     * @param ProductMetadataInterface $productMetadata
     * @param DirectoryList $directoryList
     */
    public function __construct(
        Context $context,
        protected readonly Session $session,
        protected readonly ModuleListInterface $moduleList,
        protected readonly ProductMetadataInterface $productMetadata,
        protected readonly DirectoryList $directoryList,
    ) {
        parent::__construct($context);
    }

    /**
     * Get api url
     *
     * @return string|null
     */
    public function getApiUrl(): ?string
    {
        if ($this->scopeConfig->isSetFlag(self::XML_PATH_ORLEN_IS_TEST)) {
            $url = $this->scopeConfig->getValue(self::XML_PATH_ORLEN_API_URL_TEST);
        } else {
            $url = $this->scopeConfig->getValue(self::XML_PATH_ORLEN_API_URL);
        }
        return $url ? trim($url) : null;
    }

    /**
     * Get partner ID
     *
     * @return string|null
     */
    public function getPartnerId(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ORLEN_PARTNER_ID) ?
            trim($this->scopeConfig->getValue(self::XML_PATH_ORLEN_PARTNER_ID)) : null;
    }

    /**
     * Get partner key
     *
     * @return string|null
     */
    public function getPartnerKey(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ORLEN_PARTNER_KEY) ?
            trim($this->scopeConfig->getValue(self::XML_PATH_ORLEN_PARTNER_KEY)) : null;
    }

    /**
     * Get map widget token
     *
     * @return string|null
     */
    public function getMapWidgetToken(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_MAP_WIDGET_TOKEN);
    }

    /**
     * Update shipping method
     *
     * @param string $description
     * @param string $destinationCode
     * @return void
     * @throws LocalizedException
     * @throws NoSuchEntityException
     */
    public function updateShippingMethod(string $description, string $destinationCode): void
    {
        $quote = $this->session->getQuote();
        $quote->setData('orlenpaczka_point_details', $description);
        $quote->setData('orlenpaczka_destination_code', $destinationCode);
        $quote->save();
    }

    /**
     * Is debug mode enabled
     *
     * @return bool
     */
    public function isDebug(): bool
    {
        return $this->scopeConfig->isSetFlag(self::XML_PATH_DEBUG_MODE);
    }

    /**
     * Get default box size
     *
     * @return string|null
     */
    public function getDefaultBoxSize(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_DEFAULT_BOX_SIZE);
    }

    /**
     * Get endpoint tracking
     *
     * @param string $trackingNumber
     * @return string
     */
    public function getEndpointTracking(string $trackingNumber): string
    {
        $trackingUrl = $this->scopeConfig->getValue(self::XML_PATH_TRACKING_URL);

        return $trackingUrl ? str_replace('{track}', $trackingNumber, $trackingUrl) : $trackingNumber;
    }

    /**
     * Get system info: module version, Magento version, PHP version
     *
     * @return string
     */
    public function getSystemInfo(): string
    {
        return sprintf(
            'Magento %s, Module version %s, PHP %s',
            $this->getMagentoVersion(),
            $this->getModuleVersion(),
            $this->getPhpVersion()
        );
    }

    /**
     * Get folder path for shipping protocols
     *
     * @return string
     * @throws FileSystemException
     */
    public function getShippingProtocolFolderPath(): string
    {
        return $this->directoryList->getPath(DirectoryList::VAR_DIR) . self::SHIPPING_PROTOCOL_FOLDER;
    }

    /**
     * Get address postcode
     *
     * @return string|null
     */
    public function getAddressPostcode(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_POSTCODE) ?
            trim($this->scopeConfig->getValue(self::XML_PATH_ADDRESS_POSTCODE)) : null;
    }

    /**
     * Get address city
     *
     * @return string|null
     */
    public function getAddressCity(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_CITY);
    }

    /**
     * Get address street
     *
     * @return string|null
     */
    public function getAddressStreet(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_STREET);
    }

    /**
     * Get address street number
     *
     * @return string|null
     */
    public function getAddressStreetNumber(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_STREET_NUMBER);
    }

    /**
     * Get address flat number
     *
     * @return string|null
     */
    public function getAddressFlatNumber(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_FLAT_NUMBER);
    }

    /**
     * Get address partner name
     *
     * @return string|null
     */
    public function getAddressPartnerName(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_PARTNER_NAME);
    }

    /**
     * Get address email
     *
     * @return string|null
     */
    public function getAddressEmail(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_EMAIL);
    }

    /**
     * Get address phone number
     *
     * @return string|null
     */
    public function getAddressPhoneNumber(): ?string
    {
        return $this->scopeConfig->getValue(self::XML_PATH_ADDRESS_PHONE_NUMBER);
    }

    /**
     * Get current module name
     *
     * @return string
     */
    private function getModuleName(): string
    {
        $fullClass = __CLASS__;
        $parts = explode('\\', $fullClass);

        if (count($parts) >= 2) {
            return $parts[0] . '_' . $parts[1];
        }

        return 'Unknown_Module';
    }

    /**
     * Get module name version
     *
     * @return string
     */
    private function getModuleVersion(): string
    {
        $moduleName = $this->getModuleName();
        if ($this->moduleList->has($moduleName)) {
            $module = $this->moduleList->getOne($moduleName);
            $moduleVersion = $module['setup_version'] ?? 'Unknown';
        }

        return $moduleVersion ?? 'N/A';
    }

    /**
     * Get magento version
     *
     * @return string
     */
    private function getMagentoVersion(): string
    {
        return $this->productMetadata->getVersion();
    }

    /**
     * Get php version
     *
     * @return string
     */
    private function getPhpVersion(): string
    {
        return phpversion();
    }
}
