<?php
/*
 * @package    ECLCDEK shipping plugin СДЭК to RadicalMart
 * @subpackage    ECLCDEK
 * @version    1.0.1
 * @author Econsult Lab.
 * @copyright  Copyright (c) 2025 Econsult Lab. All rights reserved.
 * @license    GNU/GPL license: https://www.gnu.org/copyleft/gpl.html
 * @link       https://econsultlab.ru
 */

namespace Joomla\Plugin\RadicalMartShipping\ECLCDEK\Helper;

use CdekSDK2\Actions\LocationRegions;
use CdekSDK2\BaseTypes\Tariff;
use CdekSDK2\BaseTypes\Tarifflist;
use CdekSDK2\Client;
use CdekSDK2\Constraints\Currencies;
use CdekSDK2\Dto\CityList as CityListDto;
use CdekSDK2\Dto\RegionList;
use CdekSDK2\Exceptions\AuthException;
use CdekSDK2\Exceptions\RequestException;
use DateTime;
use DateTimeInterface;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Plugin\RadicalMartShipping\ECLCDEK\CDEK\ECLTariffList;
use Joomla\Plugin\RadicalMartShipping\ECLCDEK\CDEK\ECLTariffListItem;
use Joomla\Registry\Registry;
use Exception;
use Symfony\Component\HttpClient\Psr18Client;
use CdekSDK2\Dto\Tariff as DtoTariff;
use CdekSDK2\Dto\TariffList as DtoTariffList;


require_once JPATH_PLUGINS . '/radicalmart_shipping/eclcdek/vendor/autoload.php';

class CDEKClientHelper
{
	/**
	 * Параметры плагина
	 * @var Registry|null
	 *
	 * @since 1.0.0
	 */
	private static ?Registry $_params = null;

	/**
	 * Объект клиента CDEK
	 * @var Client|null
	 *
	 * @since 1.0.0
	 */
	private static ?Client $_cdek = null;

	/**
	 * Получает параметры плагина
	 *
	 * @since 1.0.0
	 */
	private static function _getPluginParams(): void
	{
		if (!self::$_params instanceof Registry)
		{
			$plugin        = PluginHelper::getPlugin('radicalmart_shipping', 'eclcdek');
			self::$_params = new  Registry($plugin->params);
		}
	}

	/**
	 * Производит авторизацию в ИС СДЭК
	 *
	 * @throws RequestException
	 * @throws AuthException
	 *
	 * @since 1.0.0
	 */
	private static function _authorize(): void
	{
		if (!self::$_cdek instanceof Client)
		{
			self::_getPluginParams();
			$client      = new Psr18Client();
			self::$_cdek = new Client($client, 'account', 'secure');
			self::$_cdek->setAccount(self::$_params->get('cdek_login'));
			self::$_cdek->setSecure(self::$_params->get('cdek_pass'));
			self::$_cdek->authorize();
			self::$_cdek->getToken();
			self::$_cdek->getExpire();
		}
	}

	/**
	 * Получает список доступных стран СДЭК
	 * Формат ['Россия']['country' =>'Россия','code' => 'RU']... Отсортирован по названию страны
	 *
	 * @return object[]
	 *
	 * @since 1.0.0
	 */
	public static function getCountries(): array
	{
		$tmp = array(
			array(
				'code' => 'RU',
				'name' => 'Россия'
			)
		);

		$out = [];

		foreach ($tmp as $v)
		{
			$obj             = new \stdClass();
			$obj->code       = $v['code'];
			$obj->country    = $v['name'];
			$out[$v['name']] = $obj;
		}

		ksort($out);

		return $out;
	}

	/**
	 * Получает список регионов страны по фильтру
	 *
	 * @param   array  $filter  Фильтр на регионы. Доступные значения смотри
	 *
	 * @return RegionList|false
	 *
	 * @throws AuthException
	 * @throws RequestException
	 * @throws Exception
	 * @see       LocationRegions
	 *
	 * @since     1.0.0
	 */
	public static function getRegions(array $filter): RegionList|false
	{
		self::_authorize();
		$result = self::$_cdek->regions()->getFiltered($filter);
		if ($result->isOk())
		{
			//Запрос успешно выполнился
			return self::$_cdek->formatResponseList($result, RegionList::class);
		}

		return false;
	}

	/**
	 * Получает список городов с применением фильтра.
	 *
	 * @param   array  $filter  Массив фильтров. Значения фильтров см.
	 *
	 * @return CityListDto|false
	 *
	 * @throws AuthException
	 * @throws RequestException
	 * @throws Exception
	 * @see   LocationCities
	 *
	 * @since 1.0.0
	 */

	public static function getCities(array $filter): CityListDto|false
	{
		self::_authorize();
		$result = self::$_cdek->cities()->getFiltered($filter);
		if ($result->isOk())
		{
			//Запрос успешно выполнился
			return self::$_cdek->formatResponseList($result, CityListDto::class);
		}

		return false;
	}

	/**
	 * Получает стоимость доставки по всем доступным тарифам
	 *
	 * @param   array  $params  Параметры доставки
	 *
	 * @return ECLTariffList|null
	 *
	 * @throws AuthException
	 * @throws RequestException*@throws Exception
	 * @throws Exception
	 *
	 * @since 1.0.0
	 */

	public static function calculate(array $params): ECLTariffList|null
	{
		self::_authorize();
		$error = self::_validateCalculationParams($params);
		if ($error === true)
		{
			$tariff                = Tarifflist::create([]);
			$tariff->date          = (new DateTime())->format(DateTimeInterface::ISO8601);
			$tariff->type          = Tarifflist::TYPE_ECOMMERCE;
			$tariff->currency      = self::_translateCurrency($params['currency']);
			$tariff->lang          = Tarifflist::LANG_RUS;
			$tariff->from_location = $params['from'];
			$tariff->to_location   = $params['to'];
			$tariff->packages      = $params['goods'];
			$result                = self::$_cdek->calculator()->add($tariff);
			if ($result->hasErrors())
			{
				// Обрабатываем ошибки
				$strErr = "";
				foreach ($result->getErrors() as $error)
				{
					$strErr .= $error['code'] . ': ' . $error['message'] . "; ";
				}
				throw new Exception(Text::sprintf("PLG_RADICALMART_SHIPPING_ECLCDEK_ERROR_CALCULATE", $strErr));
			}

			if ($result->isOk())
			{
				//Запрос успешно выполнился
				//В $response будет объект \CdekSDK2\Dto\Tariff::class
				$enabled_tariffs = self::_getTariffsCodes($params);
				$tariffs         = self::$_cdek->formatResponseList($result, DtoTariffList::class);
				$out             = new ECLTariffList();
				foreach ($tariffs->tariff_codes as $i => $t)
				{
					if (is_array($enabled_tariffs) && in_array($t->tariff_code, $enabled_tariffs))
					{
						$t->delivery_sum += (float) ($params['shipping_base']->base);
						//$out->tariff_codes[] = $t;
						$item                = new ECLTariffListItem($t);
						$out->tariff_codes[] = $item;
					}
					else
					{
						// Тариф не разрешен, удаляем
						unset($tariffs->tariff_codes[$i]);
					}
				}

				return $out;
			}
		}
		else
		{
			throw $error;
		}

		return null;
	}

	/**
	 * Валидация параметров расчета стоимости доставки
	 *
	 * @param   array  $params  параметры расчета стоимости доставки
	 *
	 * @return true|Exception
	 *
	 * @since 1.0.0
	 */
	private static function _validateCalculationParams(array $params): true|Exception
	{
		return match (true)
		{
			(empty($params)) => new \Exception('Не указаны параметры'),
			(empty($params['from']->code) || empty($params['from']->country_code)) => new \Exception(Text::_("PLG_RADICALMART_SHIPPING_ECLCDEK_ERROR_FROM_ADDRESS")),
			(empty($params['to']->code) || empty($params['to']->country_code) || $params['deliveryOptions'] === 'door' && empty($params['to']->address)) => new \Exception(Text::_("PLG_RADICALMART_SHIPPING_ECLCDEK_ERROR_TO_ADDRESS")),
			($params['deliveryOptions'] === 'door' && empty($params['tariffs']->door) || $params['deliveryOptions'] === 'office' && empty($params['tariffs']->office)) => new \Exception(Text::_("PLG_RADICALMART_SHIPPING_ECLCDEK_ERROR_TARIFF")),
			(!$params['goods']) => new \Exception(Text::_("PLG_RADICALMART_SHIPPING_ECLCDEK_ERROR_GOODS")),
			default => true,
		};
	}

	/**
	 * Приведение валюты к формату СДЭК
	 *
	 * @param   string  $currency  Код валюты
	 *
	 * @return int
	 *
	 * @since 1.0.0
	 */
	private static function _translateCurrency(string $currency): int
	{
		return match (true)
		{
			$currency === 'RUB' => Currencies::RUBLE,
			$currency === 'BYN' => Currencies::BELARUSIAN_RUBLE,
			default => 0,
		};
	}

	/**
	 * Формирует массив кодов действующих тарифов
	 *
	 * @param   array  $params  параметры расчета стоимости доставки
	 *
	 * @return array|null
	 *
	 * @since 1.0.0
	 */
	private static function _getTariffsCodes(array $params): array|null
	{
		$out = null;
		switch (true)
		{
			case $params['deliveryOptions'] === 'door':
				$out = $params['tariffs']->door;
				break;
			case $params['deliveryOptions'] === 'office':
				$out = array_merge($params['tariffs']->office, $params['tariffs']->pickup);
				break;
		}

		return $out;
	}
}