<?php

declare(strict_types=1);

namespace mirzaev\huesos\models\deliveries;

// Files of the project
use mirzaev\huesos\models\core,
	mirzaev\huesos\models\traits\document as document_trait,
	mirzaev\huesos\models\interfaces\document as document_interface,
	mirzaev\huesos\models\interfaces\collection as collection_interface;

// The HTTP PSR-18 adapter for Guzzle HTTP client
use Http\Adapter\Guzzle7\Client as guzzle;

// Framework for CDEK
use CdekSDK2\Client as client,
	CdekSDK2\Dto\CityList as _cities,
	CdekSDK2\Dto\Tariff as _tariff,
	CdekSDK2\BaseTypes\Tariff as tariff,
	CdekSDK2\BaseTypes\Tarifflist as tariffs,
	CdekSDK2\Constraints\Currencies as currencies,
	CdekSDK2\BaseTypes\Location as location,
	CdekSDK2\BaseTypes\Package as package;

// Built-in libraries
use exception,
	DateTime as datetime;

/**
 * Model of CDEK
 *
 * @package mirzaev\huesos\models\deliveries
 *
 * @method cities|null location(string $name, array &$errors) Search for CDEK location by name
 *
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class cdek extends core implements document_interface, collection_interface
{
	use document_trait;

	/**
	 * Location
	 *
	 * Search for CDEK location by name (different villages, towns and farms may have the same names)
	 *
	 * @see https://api-docs.cdek.ru/182405028.html
	 * @see https://github.com/TTATPuOT/cdek-sdk2.0/blob/master/docs/index.md#получение-cписка-городов
	 *
	 * @param string $name Location name
	 * @param array &$errors Registry of errors
	 *
	 * @return _cities|null Locations, if found
	 */
	public static function location(string $name, array &$errors = []): ?_cities
	{
		try {
			if (!empty(CDEK)) {
				// Initialized CDEK account data

				// Initializing HTTP-client
				$client = new client(new guzzle, CDEK['account'], CDEK['secret']);

				// Request
				$response = $client->cities()->getFiltered(['country_codes' => 'RU', 'city' => $name]);

				if ($response->isOk()) {
					// Received response

					// Exit (success)
					return $client->formatResponseList($response, _cities::class);
				}
			}
		} catch (exception $e) {
			// Writing to the registry of errors
			$errors[] = [
				'text' => $e->getMessage(),
				'file' => $e->getFile(),
				'line' => $e->getLine(),
				'stack' => $e->getTrace()
			];
		}

		// Exit (fail)
		return null;
	}

	/**
	 * Calculate
	 *
	 * Calculate delivery from warehouse to recipient according to tariff
	 *
	 * @see https://api-docs.cdek.ru/63345430.html
	 * @see https://github.com/TTATPuOT/cdek-sdk2.0/blob/master/docs/index.md#калькулятор-расчет-по-коду-тарифа
	 *
	 * @param int $from_location Location code (sender)
	 * @param string $from_street Street (sender)
	 * @param int $to_location Location code (receiver)
	 * @param string $to_street Street (receiver)
	 * @param string $tariff Tariff (identifier)
	 * @param array $products Products that will be sent [weight, x, y, z]
	 * @param datetime $date Date of sending
	 * @param array &$errors Registry of errors
	 *
	 * @return _tariff|null Calculating data, if calculated
	 */
	public static function calculate(
		int $from_location,
		string $from_street,
		int $to_location,
		string $to_street,
		int $tariff,
		array $products,
		datetime $date,
		array &$errors = []
	): ?_tariff {
		try {
			if (!empty(CDEK)) {
				// Initialized CDEK account data

				// Initializing HTTP-client
				$client = new client(new guzzle, CDEK['account'], CDEK['secret']);

				// Initializing buffer of packages
				$packages = [];

				// Initializing packages from products
				foreach ($products as $product)
					$packages[] = package::create([
						'weight' => $product['weight'],
						'width' => $product['x'],
						'height' => $product['y'],
						'length' => $product['z']
					]);

				// Deinitializing unnecessary variables
				unset($product);

				if (!empty($packages)) {
					// Initialized packages

					// Request
					$response = $client
						->calculator()
						->add(
							tariff::create([
								'type' => tariffs::TYPE_ECOMMERCE,
								'currency' => currencies::RUBLE, // @todo globalize this
								/* 'date' => $date->format(datetime::ISO8601_EXPANDED), */
								'date' => $date->format(datetime::ISO8601),
								'lang' => tariffs::LANG_RUS, // @todo globalize this
								'tariff_code' => $tariff,
								'from_location' => location::create([
									'code' => $from_location,
									'address' => $from_street,
									'country_code' => 'RU' // @todo globalize this
								]),
								'to_location' => location::create([
									'code' => $to_location,
									'address' => $to_street,
									'country_code' => 'RU' // @todo globalize this
								]),
								'packages' => $packages
							])
						);

					if ($response->hasErrors()) {
						// Receied response with errors
					}

					if ($response->isOk()) {
						// Received response

						// Exit (success)
						return $client->formatBaseResponse($response, _tariff::class);
					}
				}

				// Deinitializing unnecessary variables
				unset($client, $packages);
			}
		} catch (exception $e) {
			// Writing to the registry of errors
			$errors[] = [
				'text' => $e->getMessage(),
				'file' => $e->getFile(),
				'line' => $e->getLine(),
				'stack' => $e->getTrace()
			];
		}

		// Exit (fail)
		return null;
	}
}
