<?php

$pluginpath = plugin_dir_path(__DIR__);

/**
 * @author The Courier Guy
 * @package tcg/core
 * @version 1.0.0
 */
class TCG_Plugin extends CustomPlugin
{

    private $parcelPerfectApi;
    private $parcelPerfectApiPayload;
    /**
     * @var array
     */
    private $parameters;

    private $logging = false;

    /**
     * @var WC_Logger
     */
    private static $log;

    /**
     * TCG_Plugin constructor.
     *
     * @param $file
     */
    public function __construct($file)
    {
        parent::__construct($file);
        $this->registerCurlControllers();
        $this->initializeParcelPerfectApi();
        $this->initializeParcelPerfectApiPayload();
        $this->registerShippingMethod();

        add_action('wp_enqueue_scripts', [$this, 'registerJavascriptResources']);
        add_action('wp_enqueue_scripts', [$this, 'registerCSSResources']);
        add_action('wp_enqueue_scripts', [$this, 'localizeJSVariables']);
        add_action('admin_enqueue_scripts', [$this, 'registerJavascriptResources']);
        add_action('admin_enqueue_scripts', [$this, 'localizeJSVariables']);
        add_action('login_enqueue_scripts', [$this, 'localizeJSVariables']);
        add_action('wp_ajax_wc_tcg_get_places', [$this, 'getSuburbs']);
        add_action('wp_ajax_nopriv_wc_tcg_get_places', [$this, 'getSuburbs']);
        add_action('woocommerce_checkout_update_order_review', [$this, 'updateShippingPropertiesFromCheckout']);
        add_action('woocommerce_checkout_update_order_review', [$this, 'removeCachedShippingPackages']);
        add_filter('woocommerce_checkout_fields', [$this, 'overrideAddressFields'], 999, 1);
        add_filter('woocommerce_checkout_fields', [$this, 'addIihtcgFields'], 10, 1);
        add_filter('woocommerce_form_field_tcg_place_lookup', [$this, 'getSuburbFormFieldMarkUp'], 1, 4);
        add_filter('woocommerce_after_shipping_calculator', [$this, 'addSuburbSelectToCart'], 10, 1);
        add_action('woocommerce_calculated_shipping', [$this, 'saveSuburbSelectFromCart']);
        add_filter('woocommerce_email_before_order_table', [$this, 'addExtraEmailFields'], 10, 3);

        add_action('woocommerce_order_actions', [$this, 'addSendCollectionActionToOrderMetaBox'], 10, 1);
        add_action('woocommerce_order_actions', [$this, 'addPrintWayBillActionToOrderMetaBox'], 10, 1);
        add_action('admin_post_print_waybill', [$this, 'printWaybillFromOrder'], 10, 0);
        add_action('woocommerce_order_action_tcg_print_waybill', [$this, 'redirectToPrintWaybillUrl'], 10, 1);
        add_filter('woocommerce_admin_shipping_fields', [$this, 'addShippingMetaToOrder'], 10, 1);

        add_action('woocommerce_order_action_tcg_send_collection', [$this, 'setCollectionFromOrder'], 10, 1);
        add_action('woocommerce_order_status_processing', [$this, 'setCollectionOnOrderProcessing']);
        add_action('woocommerce_checkout_update_order_meta', [$this, 'updateShippingPropertiesOnOrder'], 10, 2);
        add_action('woocommerce_checkout_update_order_meta', [$this, 'updateTCGServiceOnOrder'], 20, 2);

        add_action('woocommerce_shipping_packages', [$this, 'poolMultiVendorQuotes'], 20, 1);
        add_action('woocommerce_shipping_packages', [$this, 'iihtcgMethods'], 20, 1);
        add_action('woocommerce_after_calculate_totals', [$this, 'getCartTotalCost'], 20, 1);

        add_action('woocommerce_checkout_billing', [$this, 'add_shipping_selector']);

        add_filter('woocommerce_cart_no_shipping_available_html', [$this, 'change_no_shipping_text'], 20, 1); // Alters message on Cart page
        add_filter('woocommerce_no_shipping_available_html', [$this, 'change_no_shipping_text'], 20, 1); // Alters message on Checkout page

    }

    public function change_no_shipping_text() {
        return "There are no shipping options available. Try changing Area/Suburb to a different setting and then back again.";
    }

    public function iihtcgMethods($rates)
    {
        $postdata = [];
        if(isset($_POST['post_data'])){
            parse_str($_POST['post_data'], $postdata);
        }

        if(isset($postdata['iihtcg_method'])){
            switch($postdata['iihtcg_method']){
                case 'none':
                    return [];
                    break;
                case 'collect':
                    foreach($rates[0]['rates'] as $method => $rate){
                        if(strpos($method, 'local_pickup') === false){
                            unset($rates[0]['rates'][$method]);
                        }
                    }
                    break;
                case 'tcg':
                    foreach($rates[0]['rates'] as $method => $rate){
                        if(strpos($method, 'the_courier_guy') === false){
                            unset($rates[0]['rates'][$method]);
                        }
                    }
                    break;
            }
        }

        return $rates;
    }

    public function addIihtcgFields($fields)
    {
        $settings = $this->getShippingMethodSettings();

        if ($settings['enablemethodbox'] === 'yes') {
            $fields['billing']['iihtcg_method'] = [
                'label' => 'iihtcg_method',
                'type' => 'text',
                'required' => true,
                'default' => 'none',
            ];
        }

        return $fields;
    }

    /**
     * Add shipping selector
     */
    public function add_shipping_selector()
    {
        $settings = $this->getShippingMethodSettings();

        if ($settings['enablemethodbox'] === 'yes') {
            echo <<<HTML
<div class="form-row form-row-wide" id="iihtcg_selector" style="border: 1px solid lightgrey; padding:5px;">
<h3 style="margin-top: 0;">Shipping/Collection</h3>
<h5 id="please_select" style="color: #ff0000;">Please select an option to proceed</h5>
<h5 id="please_complete" style="color: red" hidden>Please complete all required fields to proceed</h5>
<span class="woocommerce-input-wrapper">
<span id="collectorder"><input type="radio" name="iihtcg_selector_input" id="iihtcg_selector_input_collect" value="collect"><span style="display:inline;margin-left 5px !important;"><strong>I will collect my order</strong></span></span>
<span style="float: right;" id="couriertome"><input type="radio" name="iihtcg_selector_input" id="iihtcg_selector_input_tcg" value="tcg"><span style="display:inline;"><strong>Courier my order to me</strong></span></span>
</span>
</div>
HTML;
        }

    }

    /**
     * Store cart total for multi-vendor in session
     * Packages are passed by vendor to shipping so cart total can't be seen
     * @param $cart
     */
    public function getCartTotalCost($cart)
    {
        if ($wc_session = WC()->session) {
            $settings = $this->getShippingMethodSettings();
            if (isset($settings['multivendor_single_override']) && $settings['multivendor_single_override'] === 'yes') {
                $wc_session->set('customer_cart_subtotal', $cart->get_subtotal() + $cart->get_subtotal_tax());
            }
        }
    }

    /**
     * @param $vendor_rates
     * @return mixed
     */
    public function poolMultiVendorQuotes($vendor_rates)
    {
        $settings = $this->getShippingMethodSettings();
        $maxRates = [];
        if (isset($settings['multivendor_single_override']) && $settings['multivendor_single_override'] === 'yes') {
            foreach ($vendor_rates as $key => $vendor_rate) {
                $maxR = 0;
                foreach ($vendor_rate['rates'] as $k => $r) {
                    if (strpos($k, 'the_courier_guy') !== false) {
                        $maxR = max($maxR, (float)($r->get_cost() + $r->get_shipping_tax()));
                    }
                }
                $maxRates[] = ['key' => $key, 'val' => $maxR];
            }
        }
        usort(
            $maxRates,
            function ($a, $b){
                if ($a['val'] === $b['val']) {
                    return 0;
                }
                return $a['val'] > $b['val'] ? -1 : 1;
            }
        );
        $cnt = 0;
        foreach ($maxRates as $maxRate) {
            if ($cnt !== 0) {
                foreach ($vendor_rates[$maxRate['key']]['rates'] as $vendor_rate) {
                    $method = $vendor_rate->get_method_id();
                    $label  = $vendor_rate->get_label();
                    if (strpos($method, 'the_courier_guy') !== false) {
                        $vendor_rate->set_cost(0);
                        $taxes = $vendor_rate->get_taxes();
                        foreach ($taxes as $key => $tax) {
                            $taxes[$key] = 0;
                        }
                        $vendor_rate->set_taxes($taxes);
                        if (strpos($label, 'Free Shipping') === false) {
                            $vendor_rate->set_label($label . ': Free Shipping');
                        }
                    }
                }
            }
            $cnt++;
        }
        return $vendor_rates;
    }

    /**
     * @param $orderId
     * @param $data
     */
    public function updateTCGServiceOnOrder($orderId, $data)
    {
        $orderShippingMethods = $data['shipping_method'];

        foreach ($orderShippingMethods as $vendorId => $orderShippingMethod) {
            if (strpos($orderShippingMethod, 'the_courier_guy') !== false) {
                $parcelPerfectApi = $this->getParcelPerfectApi();
                if ($vendorId === 0) {
                    $quoteno        = get_post_meta($orderId, '_shipping_quote')[0];
                    $shippingMethod = get_post_meta($orderId, '_shipping_method')[0];
                } else {
                    $quoteno        = get_post_meta($orderId, '_shipping_quote_' . $vendorId)[0];
                    $shippingMethod = get_post_meta($orderId, '_shipping_method_' . $vendorId)[0];
                }

                $service = explode(':', $shippingMethod)[1];
                if ($quoteno !== null) {
                    $payload = [
                        'quoteno'   => $quoteno,
                        'service'   => $service,
                        'reference' => $orderId,
                    ];
                }

                $serviceSet = false;
                $cnt        = 0;
                while (!$serviceSet && $cnt < 5) {
                    $ss = $parcelPerfectApi->setService($payload);
                    if (is_array($ss) && isset($ss[0]) && $ss[0]['quoteno'] !== '') {
                        $serviceSet = true;
                    }
                    ++$cnt;
                }
            }
        }
    }

    /**
     * @param int $orderId
     * @param array $data
     */
    public function updateShippingPropertiesOnOrder($orderId, $data)
    {
        if ($wc_session = WC()->session) {
            $customProperties = $this->getShippingCustomProperties();
            foreach ($customProperties as $key => $property) {
                update_post_meta($orderId, $key, $property);
            }

            $placeId    = $customProperties['tcg_place_id'];
            $placeLabel = $customProperties['tcg_place_label'];
            update_post_meta($orderId, '_billing_area', $placeId);
            update_post_meta($orderId, '_shipping_place', $placeLabel);
            update_post_meta($orderId, '_shipping_area', $placeId);
            update_post_meta($orderId, '_shipping_place', $placeLabel);

            $order             = new WC_Order($orderId);
            $shippingMethods   = $data['shipping_method'] ?? null;
            $orderShippingData = [];
            if (is_array($shippingMethods)) {
                foreach ($shippingMethods as $vendorId => $shippingMethod) {
                    if ($vendorId === 0) {
                        $vendor_id                    = '';
                        $orderShippingData[$vendorId] = [];

                        $response = json_encode(
                            $wc_session->get('tcg_quote_response')
                        );
                        if ($response == 'null' || strlen($response) < 3) {
                            $response = $wc_session->get('tcg_response');
                        }
                        if (isset($response) && $response !== '') {
                            $shippingMethod                                  = $shippingMethods ? array_values(
                                $shippingMethods
                            )[0] : '';
                            $orderShippingData[$vendorId]['shipping_method'] = $shippingMethod;
                            update_post_meta($orderId, '_shipping_method', $shippingMethod);
                            $quote                                       = json_decode($response, true)[0];
                            $customProperties['tcg_quoteno']             = $quote['quoteno'];
                            $orderShippingData[$vendorId]['tcg_quoteno'] = $quote['quoteno'];
                            $shippingService                             = explode(':', $shippingMethod)[1];
                            $rates                                       = $quote['rates'];
                            foreach ($rates as $service) {
                                if ($shippingService === $service['service']) {
                                    $customProperties['shippingCartage']             = $service['subtotal'];
                                    $customProperties['shippingVat']                 = $service['vat'];
                                    $customProperties['shippingTotal']               = $service['total'];
                                    $orderShippingData[$vendorId]['shippingCartage'] = $service['subtotal'];
                                    $orderShippingData[$vendorId]['shippingVat']     = $service['vat'];
                                    $orderShippingData[$vendorId]['shippingTotal']   = $service['total'];
                                }
                            }
                            update_post_meta($orderId, '_shipping_quote', $quote['quoteno']);
                        } else {
                            if($this->hasTcgShippingMethod($order)) {
                                $this->checkIfQuoteIsEmpty($order->get_shipping_country());
                            }
                        }
                        $order->add_order_note('Shipping quotes for order: ' . $response);
                        if (isset($customProperties['shippingCartage'])) {
                            $shipping = <<<SHIPPING
  Cartage of $customProperties[shippingCartage]
  + VAT of $customProperties[shippingVat]
  = Total of $customProperties[shippingTotal]
  (before merchant markup)
SHIPPING;
                            $order->add_order_note('Shipping by selected method: ' . $shipping);
                        }
                    } else {
                        $vendor_id                    = $vendorId;
                        $orderShippingData[$vendorId] = [];
                        $vendorId                     = $vendorId === 0 ? '' : $vendorId;
                        $response                     = json_encode(
                            $wc_session->get('tcg_quote_response' . $vendorId)
                        );
                        if ($response == 'null' || strlen($response) < 3) {
                            $response = $wc_session->get('tcg_response' . $vendorId);
                        }
                        if (isset($response) && $response !== '') {
                            $shippingMethod                                  = $shippingMethods ? $shippingMethods[$vendorId] : '';
                            $orderShippingData[$vendorId]['shipping_method'] = $shippingMethod;
                            update_post_meta($orderId, '_shipping_method_' . $vendorId, $shippingMethod);
                            $quote                                        = json_decode($response, true)[0];
                            $customProperties['tcg_quoteno_' . $vendorId] = $quote['quoteno'];
                            $orderShippingData[$vendorId]['tcg_quoteno']  = $quote['quoteno'];
                            $shippingService                              = explode(':', $shippingMethod)[1];
                            $rates                                        = $quote['rates'];
                            foreach ($rates as $service) {
                                if ($shippingService === $service['service']) {
                                    $customProperties['shippingCartage_' . $vendorId] = $service['subtotal'];
                                    $customProperties['shippingVat_' . $vendorId]     = $service['vat'];
                                    $customProperties['shippingTotal_' . $vendorId]   = $service['total'];
                                    $orderShippingData[$vendorId]['shippingCartage']  = $service['subtotal'];
                                    $orderShippingData[$vendorId]['shippingVat']      = $service['vat'];
                                    $orderShippingData[$vendorId]['shippingTotal']    = $service['total'];
                                }
                            }
                            update_post_meta($orderId, '_shipping_quote_' . $vendorId, $quote['quoteno']);
                        } else {
                            if($this->hasTcgShippingMethod($order)) {
                                $this->checkIfQuoteIsEmpty($order->get_shipping_country());
                            }
                        }
                        $shippingCartage = 'shippingCartage_' . $vendorId;
                        $shippingVat     = 'shippingVat_' . $vendorId;
                        $shippingTotal   = 'shippingTotal_' . $vendorId;
                        $order->add_order_note('Shipping quotes for order: ' . $response);
                        $shipping = <<<SHIPPING
  Cartage of $shippingCartage
  + VAT of $shippingVat
  = Total of $shippingTotal
  (before merchant markup)
SHIPPING;
                        $order->add_order_note('Shipping by selected method: ' . $shipping);
                    }

                    // Clear the cached prohibited product settings
                    $wc_session->set('tcg_prohibited_vendor' . $vendor_id, '');
                }
            }

            update_post_meta($orderId, '_order_shipping_data', json_encode($orderShippingData));

            $order->calculate_totals();
            $order->save();
            $order->add_order_note('Order shipping total on order: ' . $order->get_shipping_total());
            $order->save();
            $order->save_meta_data();
            $this->clearShippingCustomProperties();
            $wc_session->set('customer_cart_subtotal', '');
        }
    }

    /**
     * @param string $postData
     */
    private function checkIfQuoteIsEmpty($country)
    {
        $shippingMethodSettings = $this->getShippingMethodSettings();
        if (!empty($shippingMethodSettings) && !empty($shippingMethodSettings['south_africa_only']) && $shippingMethodSettings['south_africa_only'] == 'yes' && $country != 'ZA') {
            // do nothing
        } else {
            WC()->cart->calculate_shipping();
            WC()->cart->calculate_totals();
            // Abort the order and return to the checkout page
            echo '{"result":"failure","messages":"<ul class=\"woocommerce-error\" role=\"alert\">\n\t\t\t<li data-id=\"billing_tcg_place_lookup\">\n\t\t\t<strong>Failed to add shipping.<\/strong> Try changing Area/Suburb to a different setting and then back again.\t\t<\/li>\n\t<\/ul>\n","refresh":true,"reload":false}';
            exit;
        }
    }

    /**
     * @param string $postData
     */
    public function updateShippingPropertiesFromCheckout($postData)
    {
        if ($wc_session = WC()->session) {
            parse_str($postData, $parameters);

            $addressPrefix = 'shipping_';
            if (!isset($parameters['ship_to_different_address']) || $parameters['ship_to_different_address'] != true) {
                $addressPrefix = 'billing_';
            }
            $insurance = false;
            if (!empty($parameters[$addressPrefix . 'insurance']) && $parameters[$addressPrefix . 'insurance'] == '1') {
                $insurance = true;
            }
            $shippingMethods  = isset($parameters['shipping_method']) ? $parameters['shipping_method'] : null;
            $customProperties = [
                'tcg_place_id'    => isset($parameters[$addressPrefix . 'tcg_place_lookup_place_id']) ? sanitize_text_field(
                    $parameters[$addressPrefix . 'tcg_place_lookup_place_id']
                ) : '',
                'tcg_place_label' => isset($parameters[$addressPrefix . 'tcg_place_lookup_place_label']) ? sanitize_text_field(
                    $parameters[$addressPrefix . 'tcg_place_lookup_place_label']
                ) : '',
                'tcg_insurance'   => $insurance,

            ];

            if (is_array($shippingMethods)) {
                foreach ($shippingMethods as $vendorId => $shippingMethod) {
                    if ($vendorId === 0) {
                        $customProperties['tcg_shipping_method'] = $shippingMethod;
                        $qn                                      = json_encode(
                            $wc_session->get('tcg_quote_response')
                        );
                        if ($qn == 'null' || strlen($qn) < 3) {
                            $qn = $wc_session->get('tcg_response');
                        }
                        if (isset($qn) && strlen($qn) > 2) {
                            $quote = json_decode($qn, true);
                            if (isset($quote[0])) {
                                $customProperties['tcg_quoteno'] = $quote[0]['quoteno'];
                                $shippingService                 = explode(':', $shippingMethod)[1];
                                $rates                           = $quote[0]['rates'];
                                foreach ($rates as $service) {
                                    if ($shippingService === $service['service']) {
                                        $customProperties['shippingCartage'] = $service['subtotal'];
                                        $customProperties['shippingVat']     = $service['vat'];
                                        $customProperties['shippingTotal']   = $service['total'];
                                    }
                                }
                            }
                        }
                    } else {
                        $customProperties['tcg_shipping_method_' . $vendorId] = $shippingMethod;
                        $vendorId                                             = $vendorId === 0 ? '' : $vendorId;
                        $qn                                                   = json_encode(
                            $wc_session->get('tcg_quote_response' . $vendorId)
                        );
                        if ($qn == 'null' || strlen($qn) < 3) {
                            $qn = $wc_session->get('tcg_response' . $vendorId);
                        }
                        if (isset($qn) && strlen($qn) > 2) {
                            $quote = json_decode($qn, true);
                            if (isset($quote[0])) {
                                $customProperties['tcg_quoteno_' . $vendorId] = $quote[0]['quoteno'];
                                $shippingService                              = explode(':', $shippingMethod)[1];
                                $rates                                        = $quote[0]['rates'];
                                foreach ($rates as $service) {
                                    if ($shippingService === $service['service']) {
                                        $customProperties['shippingCartage_' . $vendorId] = $service['subtotal'];
                                        $customProperties['shippingVat_' . $vendorId]     = $service['vat'];
                                        $customProperties['shippingTotal_' . $vendorId]   = $service['total'];
                                    }
                                }
                            }
                        }
                    }
                }
            }

            $this->setShippingCustomProperties($customProperties);
        }
    }

    /**
     * @return array
     */
    public function getShippingCustomProperties($order = null)
    {
        $result = [];
        if ($wc_session = WC()->session) {
            $customProperties = $wc_session->get('custom_properties');
            if ($customProperties && is_array($customProperties)) {
                foreach ($customProperties as $customProperty) {
                    $result[$customProperty] = $wc_session->get($customProperty);
                }
            }
        }

        return $result;
    }

    /**
     * @param $order
     * @param $sent_to_admin
     * @param $plain_text
     *
     * @return mixed
     */
    public function addExtraEmailFields($order, $sent_to_admin, $plain_text)
    {
        // Check to see if this is a TCG shipping method
        if ($this->hasTcgShippingMethod($order)) {
            global $wpdb;
            $query   = "select meta_value from $wpdb->postmeta where post_id = %s and meta_key = 'dawpro_waybill'";
            $waybill = $wpdb->get_results($wpdb->prepare($query, [$order->get_id()]));
            if ($waybill && count($waybill) > 0) {
                $waybillNo = $waybill[0]->meta_value;
                echo <<<HTML
<br><br><span>Your Waybill: $waybillNo <a href="https://thecourierguy.pperfect.com/?w=$waybillNo">
  Click me to track</a></span><br><br>
HTML;
            }
        } else {
            return;
        }
    }

    /**
     * @param WC_Order $order
     */
    public function setCollectionFromOrder($order)
    {
        $forceCollectionSending = true;
        $this->setCollection($order, $forceCollectionSending);
    }

    /**
     * @param int $orderId
     */
    public function setCollectionOnOrderProcessing($orderId)
    {
        $order = new WC_Order($orderId);
        $this->setCollection($order);
    }

    /**
     * @param array $adminShippingFields
     *
     * @return array
     */
    public function addShippingMetaToOrder($adminShippingFields = [])
    {
        $tcgAdminShippingFields = [
            /*'insurance' => [
            'label' => __('Courier Guy Insurance'),
            'class' => 'wide',
            'show' => true,
            'readonly' => true,
            'type', 'checkbox'
            ],*/
            'area'  => [
                'label'             => __('Courier Guy Shipping Area Code'),
                'wrapper_class'     => 'form-field-wide',
                'show'              => true,
                'custom_attributes' => [
                    'disabled' => 'disabled',
                ],
            ],
            'place' => [
                'label'             => __('Courier Guy Shipping Area Description'),
                'wrapper_class'     => 'form-field-wide',
                'show'              => true,
                'custom_attributes' => [
                    'disabled' => 'disabled',
                ],
            ],
        ];

        return array_merge($adminShippingFields, $tcgAdminShippingFields);
    }

    /**
     * @param WC_Order $order
     */
    public function redirectToPrintWaybillUrl($order)
    {
        wp_redirect('/wp-admin/admin-post.php?action=print_waybill&order_id=' . $order->get_id());
        exit;
    }

    /**
     * Latest API functionality downloads the waybill created by TCG
     * We no longer need to develop the waybill within the plugin
     */
    public function printWaybillFromOrder()
    {
        $uploadsDirectory = $this->getPluginUploadPath();
        $orderId          = filter_var($_GET['order_id'], FILTER_SANITIZE_NUMBER_INT);

        $waybills = get_post_meta($orderId, 'dawpro_waybill', true);
        if ($waybills && strlen($waybills) > 0) {
            $waybills = explode(',', $waybills);
        }

        if ($waybills && count($waybills) > 0) {
            $pdfFilePaths = [];
            foreach ($waybills as $waybill) {
                $waybill = trim($waybill);
                if (!empty($uploadsDirectory)) {
                    $pdfFilePath = $uploadsDirectory . '/' . $waybill . '.pdf';
                    if (file_exists($pdfFilePath)) {
                        $pdfFilePaths[] = $pdfFilePath;
                    }
                }
            }
            if (count($pdfFilePaths) > 0) {
                if (count($pdfFilePaths) === 1) {
                    $this->sendPdf($pdfFilePaths);
                } else {
                    $zipFile = $uploadsDirectory . '/' . $orderId . '.zip';
                    $this->sendPdf($pdfFilePaths, $zipFile);
                }
            }
        }
    }

    /**
     * @param array $actions
     *
     * @return mixed
     */
    public function addPrintWayBillActionToOrderMetaBox($actions)
    {
        $orderId           = sanitize_text_field($_GET['post']);
        $order             = wc_get_order($orderId);
        $hasShippingMethod = $this->hasTcgShippingMethod($order);
        $waybill           = get_post_meta($orderId, 'dawpro_waybill');
        if ($hasShippingMethod && count($waybill) > 0) {
            $actions['tcg_print_waybill'] = __('Print Waybill', 'woocommerce');
        }

        return $actions;
    }

    /**
     * @param array $actions
     *
     * @return mixed
     */
    public function addSendCollectionActionToOrderMetaBox($actions)
    {
        $orderId           = sanitize_text_field($_GET['post']);
        $order             = wc_get_order($orderId);
        $hasShippingMethod = $this->hasTcgShippingMethod($order);
        $waybill           = get_post_meta($orderId, 'dawpro_waybill');
        if ($hasShippingMethod && count($waybill) === 0) {
            $actions['tcg_send_collection'] = __('Send Order to Courier Guy', 'woocommerce');
        }

        return $actions;
    }

    /**
     * @param $field
     * @param $key
     * @param $args
     * @param $value
     *
     * @return string
     */
    public function getSuburbFormFieldMarkUp($field, $key, $args, $value)
    {
        //@todo The contents of this method is legacy code from an older version of the plugin.
        if ($args['required']) {
            $args['class'][] = 'validate-required';
            $required        = ' <abbr class="required" title="' . esc_attr__('required', 'woocommerce') . '">*</abbr>';
        } else {
            $required = '';
        }
        $options                  = $field = '';
        $label_id                 = $args['id'];
        $sort                     = $args['priority'] ? $args['priority'] : '';
        $field_container          = '<p class="form-row %1$s" id="%2$s" data-sort="' . esc_attr($sort) . '">%3$s</p>';
        $customShippingProperties = $this->getShippingCustomProperties();
        $option_key               = isset($customShippingProperties['tcg_place_id']) ? $customShippingProperties['tcg_place_id'] : '';
        $option_text              = isset($customShippingProperties['tcg_place_label']) ? $customShippingProperties['tcg_place_label'] : '';
        $options                  .= '<option value="' . esc_attr($option_key) . '" ' . selected(
                $value,
                $option_key,
                false
            ) . '>' . esc_attr($option_text) . '</option>';
        $field                    .= '<input type="hidden" name="' . esc_attr(
                $key
            ) . '_place_id" value="' . $option_key . '"/>';
        $field                    .= '<input type="hidden" name="' . esc_attr(
                $key
            ) . '_place_label" value="' . $option_text . '"/>';
        $field                    .= '<select id="' . esc_attr($args['id']) . '" name="' . esc_attr(
                $args['id']
            ) . '" class="select ' . esc_attr(
                implode(' ', $args['input_class'])
            ) . '" ' . ' data-placeholder="' . esc_attr($args['placeholder']) . '">
                            ' . $options . '
                        </select>';
        if (!empty($field)) {
            $field_html = '';
            if ($args['label'] && 'checkbox' != $args['type']) {
                $field_html .= '<label for="' . esc_attr($label_id) . '" class="' . esc_attr(
                        implode(' ', $args['label_class'])
                    ) . '">' . $args['label'] . $required . '</label>';
            }
            $field_html .= $field;
            if ($args['description']) {
                $field_html .= '<span class="description">' . esc_html($args['description']) . '</span>';
            }
            $container_class = esc_attr(implode(' ', $args['class']));
            $container_id    = esc_attr($args['id']) . '_field';
            $field           = sprintf($field_container, $container_class, $container_id, $field_html);
        }

        return $field;
    }

    /**
     * @param array $fields
     *
     * @return array
     */
    public function overrideAddressFields($fields)
    {
        $fields = $this->addAddressFields('billing', $fields);
        $fields = $this->addAddressFields('shipping', $fields);

        return $fields;
    }

    /**
     *
     */
    public function removeCachedShippingPackages()
    {
        //@todo The contents of this method is legacy code from an older version of the plugin.
        $packages = WC()->cart->get_shipping_packages();
        if ($wc_session = WC()->session) {
            foreach ($packages as $key => $value) {
                $shipping_session = "shipping_for_package_$key";
                $wc_session->set($shipping_session, '');
            }
        }
    }

    /**
     *
     */
    public function getSuburbs()
    {
        //@todo The contents of this method is legacy code from an older version of the plugin.
        $term        = sanitize_text_field($_GET['q']['term']);
        $dp_areas    = [];
        $payloadData = [
            'name' => $term,
        ];
        $d           = $this->getPlacesByName($payloadData);
        foreach ($d as $result) {
            $suggestion = [
                'suburb_value' => $result['town'],
                'suburb_key'   => $result['place'],
            ];
            $dp_areas[] = $suggestion;
        }
        echo json_encode($dp_areas);
        exit;
    }

    /**
     *
     */
    public function localizeJSVariables()
    {
        //@todo The contents of this method is legacy code from an older version of the plugin, however slightly refactored.
        $southAfricaOnly        = false;
        $shippingMethodSettings = $this->getShippingMethodSettings();
        if (!empty($shippingMethodSettings) && !empty($shippingMethodSettings['south_africa_only']) && $shippingMethodSettings['south_africa_only'] == 'yes') {
            $southAfricaOnly = true;
        }
        $translation_array = [
            'url'             => get_admin_url(null, 'admin-ajax.php'),
            'southAfricaOnly' => ($southAfricaOnly) ? 'true' : 'false',
        ];
        wp_localize_script($this->getPluginTextDomain() . '-main434.js', 'theCourierGuy', $translation_array);
    }

    /**
     *
     */
    public function registerJavascriptResources()
    {
        $this->registerJavascriptResource('main434.js', ['jquery']);
    }

    /**
     *
     */
    public function registerCSSResources()
    {
        $this->registerCSSResource('main434.css');
    }

    /**
     *
     */
    public function addSuburbSelectToCart()
    {
        $customShippingProperties = $this->getShippingCustomProperties();
        if (isset($customShippingProperties) && count($customShippingProperties) > 0) {
            $placeId    = $customShippingProperties['tcg_place_id'];
            $placeLabel = $customShippingProperties['tcg_place_label'];
            $options    = [
                $placeId => $placeLabel,
            ];
            ob_start();
            ?>
            <p class="form-row form-row-wide tcg-suburb-field" style="display: none;"
               id="tcg-cart-shipping-area-panel">
                <input type="hidden" name="tcg_place_id"/>
                <input type="hidden" name="tcg_place_label"/>
                <select class="select form-row-wide" style="width:100%;">
                    <?php
                    foreach ((array)$options as $option_key => $option_value): ?>
                        <option value="<?php
                        echo esc_attr($option_key); ?>" <?php
                        selected($option_key, esc_attr('4509')); ?>><?php
                            echo esc_attr($option_value); ?></option>
                    <?php
                    endforeach; ?>
                </select>
            </p>
            <?php
            echo ob_get_clean();
        }
    }

    /**
     *
     */
    public function saveSuburbSelectFromCart()
    {
        if (!empty($_POST['tcg_place_id']) && !empty($_POST['tcg_place_label']) && !empty($_POST['woocommerce-shipping-calculator-nonce']) && wp_verify_nonce(
                $_POST['woocommerce-shipping-calculator-nonce'],
                'woocommerce-shipping-calculator'
            )) {
            $customProperties = [
                'tcg_place_id'    => sanitize_text_field($_POST['tcg_place_id']),
                'tcg_place_label' => sanitize_text_field($_POST['tcg_place_label']),
            ];
            $this->setShippingCustomProperties($customProperties);
            $this->removeCachedShippingPackages();
        }
    }

    /**
     * @param array $package
     * @param array $parameters
     *
     * @return array|mixed|object
     */
    public function getQuote($package, $parameters)
    {
        $this->parameters = $parameters;
        $this->logging    = isset($this->parameters['usemonolog']) ? $this->parameters['usemonolog'] === 'yes' : false;
        if ($this->logging && self::$log === null) {
            self::$log = wc_get_logger();
        }

        $postData  = [];
        $addresses = [];
        isset($_POST['post_data']) ? parse_str($_POST['post_data'], $postData) : '';
        isset($_POST['val']) ? parse_str($_POST['val'], $addresses) : '';

        if (!empty($postData)) {
            $package['destination']['tcg_place'] = $postData['billing_tcg_place_lookup'];
        }

        $result                  = [];
        $parcelPerfectApiPayload = $this->getParcelPerfectApiPayload();
        $quoteParams             = $parcelPerfectApiPayload->getQuotePayload($package, $parameters);

        if (count($postData) > 0 && isset($quoteParams['details'])) {
            if ($quoteParams['details']['destpercontact'] !== ($postData['billing_first_name'] . ' ' . $postData['billing_last_name'])) {
                $quoteParams['details']['destpercontact'] = $postData['billing_first_name'] . ' ' . $postData['billing_last_name'];
            }
            if ($quoteParams['details']['destperphone'] !== ($postData['billing_phone'])) {
                $quoteParams['details']['destperphone'] = $postData['billing_phone'];
            }
        }

        if (count($addresses) > 0) {
            $quoteParams['details']['destpercontact'] = isset($quoteParams['details']) ? $addresses['val'] : '';
        }

        if(self::$log){
            self::$log->add('thecourierguy', 'Before MV params: ' . json_encode($quoteParams));
        }
        $payloadData = apply_filters('thecourierguy_before_request_quote', $quoteParams, $package);
        if(self::$log){
            self::$log->add('thecourierguy', 'After MV payloaddata: ' . json_encode($payloadData));
        }

        // Store vendor ids if multi vendor store
        $vendorId = '';
        if (isset($package['vendor_id'])) {
            $vendorId = $package['vendor_id'];
        }

        if (!empty($payloadData) && !empty($payloadData['details']) && !empty($payloadData['details']['origplace']) && !empty($payloadData['details']['origtown']) && !empty($payloadData['details']['destplace']) && !empty($payloadData['details']['desttown'])) {
            if (!$this->compareCachedQuoteRequest($payloadData, $vendorId)) {
                $parcelPerfectApi = $this->getParcelPerfectApi();
                $result           = $parcelPerfectApi->getQuote($payloadData);
                if (is_array($result) && isset($result[0]['quoteno']) && $result[0]['quoteno'] !== '') {
                    $this->updateCachedQuoteRequest($payloadData, $vendorId);
                    $this->updateCachedQuoteResponse($result, $vendorId);
                } else {
                    $this->updateCachedQuoteRequest([], '');
                    $this->updateCachedQuoteResponse([], '');
                }
            } else {
                $result = json_decode($this->getCachedQuoteResponse($vendorId), true);
            }
        } else {
            $this->clearCachedQuote($vendorId);
        }

        return $result;
    }

    /**
     * @param string $quoteNumber
     */
    public function setService($quoteNumber)
    {
        global $wpdb;

        if (!empty($quoteNumber) && $wc_session = WC()->session) {
            $parcelPerfectApi = $this->getParcelPerfectApi();
            $chosenMethod     = $wc_session->get('chosen_shipping_methods');
            if (!empty($chosenMethod) && !empty($chosenMethod[0])) {
                $serviceType = $chosenMethod[0];
                if (!empty($serviceType)) {
                    $methodParts = explode(':', $serviceType);
                    if (!empty($methodParts) && !empty($methodParts[0]) && !empty($methodParts[1]) && $methodParts[0] == 'the_courier_guy') {
                        $service = $methodParts[1];
                        if (!empty($service)) {
                            $payloadData = [
                                'quoteno' => $quoteNumber,
                                'service' => $service,
                            ];

                            $query   = "select * from $wpdb->postmeta where meta_key = '_shipping_quote' and meta_value = %s";
                            $orderId = $wpdb->get_results($wpdb->prepare($query, [$quoteNumber]));
                            if ($orderId && count($orderId) > 0) {
                                $orderId                  = $orderId[0]->post_id;
                                $payloadData['reference'] = $orderId;
                            }
                            $parcelPerfectApi->setService($payloadData);
                        }
                    }
                }
            }
        }
    }

    /**
     *
     */
    protected function registerModel()
    {
        require_once $this->getPluginPath() . 'Model/Product.php';
    }

    /**
     *
     */
    private function clearShippingCustomProperties()
    {
        if ($wc_session = WC()->session) {
            $customSettings = $wc_session->get('custom_properties');
            foreach ($customSettings as $customSetting) {
                $wc_session->set($customSetting, '');
            }
        }
    }

    /**
     * @param array $customProperties
     */
    private function setShippingCustomProperties($customProperties)
    {
        if ($wc_session = WC()->session) {
            $properties = [];
            foreach ($customProperties as $key => $customProperty) {
                $properties[] = $key;
                $wc_session->set($key, filter_var($customProperty, FILTER_SANITIZE_STRING));
            }
            $wc_session->set('custom_properties', $properties);
        }
    }

    /**
     * @param $filePaths
     * @param null $zipfile
     */
    private function sendPdf($filePaths, $zipfile = null)
    {
        if (!$zipfile) {
            header('Content-type: application/pdf');
            header('Content-Disposition: inline; filename="' . basename($filePaths[0]) . '"');
            header('Content-Transfer-Encoding: binary');
            header('Content-Length: ' . filesize($filePaths[0]));
            header('Accept-Ranges: bytes');
            @readfile($filePaths[0]);
            exit;
        } else {
            $zip = new ZipArchive();
            if ($zip->open($zipfile, ZipArchive::CREATE) !== true) {
                die('Could not create zip file ' . $zipfile);
            }
            foreach ($filePaths as $filePath) {
                $zip->addFile($filePath, basename($filePath));
            }
            $zip->close();
            if (file_exists($zipfile)) {
                header('Content-type: application/zip');
                header('Content-Disposition: inline; filename="' . basename($zipfile) . '"');
                @readfile($zipfile);
                unlink($zipfile);
                exit;
            }
        }
        exit;
    }

    private function getShippingMethodSettings()
    {
        $shippingMethodSettings = [];
        $existingZones          = WC_Shipping_Zones::get_zones();
        foreach ($existingZones as $zone) {
            $shippingMethods = $zone['shipping_methods'];
            foreach ($shippingMethods as $shippingMethod) {
                if ($shippingMethod->id == 'the_courier_guy') {
                    $courierGuyShippingMethod = $shippingMethod;
                }
            }
        }
        if (!empty($courierGuyShippingMethod)) {
            $shippingMethodSettings = $courierGuyShippingMethod->instance_settings;
        }

        return $shippingMethodSettings;
    }

    /**
     * @param array $source
     * @param array $target
     * @param string $positionIndex
     *
     * @return array
     */
    private function insertValueAfterPosition($source, $target, $positionIndex)
    {
        $result = [];
        foreach ($source as $sourceKey => $sourceValue) {
            $result[$sourceKey] = $sourceValue;
            if ($sourceKey == $positionIndex) {
                foreach ($target as $targetKey => $targetValue) {
                    $result[$targetKey] = $targetValue;
                }
            }
        }

        return $result;
    }

    private function getSurburbLabel()
    {
        $shippingMethodSettings = $this->getShippingMethodSettings();
        if (!empty($shippingMethodSettings)) {
            if (!empty($shippingMethodSettings['Suburb_title'])) {
                return $shippingMethodSettings['Suburb_title'];
            } else {
                return 'Area / Suburb';
            }
        }

        return 'Area / Suburb';
    }

    private function getSurburblocation()
    {
        $shippingMethodSettings = $this->getShippingMethodSettings();
        if (!empty($shippingMethodSettings)) {
            if (!empty($shippingMethodSettings['Suburb_location'])) {
                return $shippingMethodSettings['Suburb_location'];
            } else {
                return '_country';
            }
        }

        return '_country';
    }

    /**
     * @param string $addressType
     * @param array $fields
     *
     * @return array
     */
    private function addAddressFields($addressType, $fields)
    {
        $addressFields          = $fields[$addressType];
        $required               = false;
        $shippingMethodSettings = $this->getShippingMethodSettings();
        if (!empty($shippingMethodSettings) && !empty($shippingMethodSettings['south_africa_only']) && $shippingMethodSettings['south_africa_only'] == 'yes') {
            $required = false;
        }
        $addressFields = $this->insertValueAfterPosition(
            $addressFields,
            [
                $addressType . '_tcg_place_lookup' => [
                    'type'        => 'tcg_place_lookup',
                    'label'       => $this->getSurburbLabel(),
                    'options'     => ['Search Suburb...'],
                    'required'    => $required,
                    'placeholder' => $this->getSurburbLabel(),
                    'class'       => ['form-row-wide', 'address-field', 'tcg-suburb-field'],
                ],
            ],
            $addressType . $this->getSurburblocation()
        );
        $addressFields = array_merge(
            $addressFields,
            [
                $addressType . '_postcode' => [
                    'type'     => 'text',
                    'label'    => 'Postcode',
                    'required' => true,
                    'class'    => ['form-row-last'],
                ],
            ]
        );
        if (isset($shippingMethodSettings['billing_insurance']) && $shippingMethodSettings['billing_insurance'] === 'yes') {
            $addressFields[$addressType . '_insurance'] = [
                'type'     => 'checkbox',
                'label'    => 'Would you like to include Shipping Insurance',
                'required' => false,
                'class'    => ['form-row-wide', 'tcg-insurance'],
                'priority' => 90,
            ];
        }
        $addressFields[$addressType . '_tcg_quoteno'] = [
            'type'     => 'text',
            'label'    => 'TCG Quote Number',
            'required' => false,
            'class'    => ['form-row-wide', 'tcg-quoteno'],
            'priority' => 90,
        ];
        $legacyFieldProperties                        = [
            'type'     => 'hidden',
            'required' => false,
        ];
        //@todo The setting of these additional billing and shipping properties is legacy from an older version of the plugin. This is to override legacy properties to invalidate cached required validation.
        $addressFields[$addressType . '_area']  = $legacyFieldProperties;
        $addressFields[$addressType . '_place'] = $legacyFieldProperties;
        $fields[$addressType]                   = $addressFields;

        return $fields;
    }

    /**
     * @return mixed
     */
    private function getParcelPerfectApi()
    {
        return $this->parcelPerfectApi;
    }

    /**
     * @param mixed $parcelPerfectApi
     */
    private function setParcelPerfectApi($parcelPerfectApi)
    {
        $this->parcelPerfectApi = $parcelPerfectApi;
    }

    /**
     *
     */
    private function initializeParcelPerfectApi()
    {
        $emailAddress = $this->getEmailAddress();
        $password     = $this->getPassword();
        require_once $this->getPluginPath() . 'Core/ParcelPerfectApi.php';
        $parcelPerfectApi = new ParcelPerfectApi($emailAddress, $password);
        $this->setParcelPerfectApi($parcelPerfectApi);
    }

    /**
     * @return mixed
     */
    private function getEmailAddress()
    {
        return get_option('tcg_username');
    }

    /**
     * @return mixed
     */
    private function getPassword()
    {
        return get_option('tcg_password');
    }

    /**
     * @return mixed
     */
    private function getParcelPerfectApiPayload()
    {
        return $this->parcelPerfectApiPayload;
    }

    /**
     * @param mixed $parcelPerfectApiPayload
     */
    private function setParcelPerfectApiPayload($parcelPerfectApiPayload)
    {
        $this->parcelPerfectApiPayload = $parcelPerfectApiPayload;
    }

    /**
     *
     */
    private function initializeParcelPerfectApiPayload()
    {
        require_once $this->getPluginPath() . 'Core/ParcelPerfectApiPayload.php';
        $parcelPerfectApiPayload = new ParcelPerfectApiPayload();
        $this->setParcelPerfectApiPayload($parcelPerfectApiPayload);
    }

    /**
     *
     */
    private function registerCurlControllers()
    {
        require_once $this->getPluginPath() . 'Core/CurlControllers.php';
    }

    /**
     *
     */
    private function registerShippingMethod()
    {
        require_once $this->getPluginPath() . 'Shipping/TCG_ShippingMethod.php';
        add_filter(
            'woocommerce_shipping_methods',
            function ($methods){
                $methods['the_courier_guy'] = 'TCG_Shipping_Method';

                return $methods;
            }
        );
    }

    /**
     * @param array $payloadData
     *
     * @return mixed
     */
    private function getPlacesByName(array $payloadData)
    {
        $parcelPerfectApi = $this->getParcelPerfectApi();

        return $parcelPerfectApi->getPlacesByName($payloadData);
    }

    /**
     * @param WC_Order $order
     * @param bool $forceCollectionSending
     */
    private function setCollection(WC_Order $order, bool $forceCollectionSending = false)
    {
        $orderId           = $order->get_id();
        $orderShippingData = get_post_meta($orderId, '_order_shipping_data', true);
        if ($orderShippingData != '') {
            $orderShippingData = json_decode($orderShippingData, true);
        }
        $dawproCollectnos = get_post_meta($orderId, 'dawpro_collectno', true);
        if ($dawproCollectnos != '') {
            $dawproCollectnos = explode(',', $dawproCollectnos);
        }

        if (is_array($dawproCollectnos) && is_array($orderShippingData) && (count($dawproCollectnos) >= count(
                    $orderShippingData
                ))) {
            exit();
        }

        $sendyOrderShippingData = [];
        foreach ($orderShippingData as $vendorId => $shippingMethod) {
            $shipping_method_id = $shippingMethod['shipping_method'];
            if (strpos($shipping_method_id, 'the_courier_guy') !== false) {
                $sendyOrderShippingData[$vendorId] = $shippingMethod;
            }
        }

        if (is_array($dawproCollectnos) && (count($dawproCollectnos) >= count($sendyOrderShippingData))) {
            exit();
        }

        $orderCollectNos = [];
        $orderCollectNos = is_string(get_post_meta($orderId, 'sendy_order_collections', true)) ? [] : get_post_meta(
            $orderId,
            'sendy_order_collections',
            true
        );

        foreach ($sendyOrderShippingData as $vendorId => $shippingMethod) {
            $shipping_method_id = $shippingMethod['shipping_method'];
            $parts              = explode(':', $shipping_method_id);
            if (!array_key_exists($shipping_method_id, $orderCollectNos)) {
                if (is_array($parts) && count($parts) === 3) {
                    $shippingInstanceId                 = $parts[2];
                    $parcelPerfectApi                   = $this->getParcelPerfectApi();
                    $parameters                         = get_option(
                        'woocommerce_the_courier_guy_' . $shippingInstanceId . '_settings'
                    );
                    $payloadData                        = [];
                    $payloadData['quoteno']             = $shippingMethod['tcg_quoteno'];
                    $payloadData['quoteCollectionDate'] = '';
                    $payloadData['starttime']           = (new DateTime('now', new DateTimeZone('Africa/Johannesburg')))->add(new DateInterval('PT1H'))->format(
                        'H:i:s'
                    );
                    $payloadData['endtime']             = '18:00:00';
                    $payloadData['printWaybill']        = 1;
                    if ($forceCollectionSending || (!empty($parameters['automatically_submit_collection_order']) && $parameters['automatically_submit_collection_order'] == 'yes')) {
                        $cnt     = 0;
                        $success = false;
                        while (!$success && $cnt < 5) {
                            $result = $parcelPerfectApi->setCollection($payloadData);
                            if (isset($result) && is_array($result) && count($result) === 1) {
                                $orderCollectNos[$shipping_method_id] = $result[0]['waybillno'];
                                $this->updateOrderWaybill($order, $result[0]['waybillno']);
                                $this->updateOrderCollectionNumber($order, $result[0]['collectno']);
                                $this->savePdfWaybill($result[0]['waybillno'], $result[0]['waybillBase64']);
                                $success = true;
                            }
                            $cnt++;
                        }
                    }
                    $vendorId = $vendorId === 0 ? '' : $vendorId;
                    $this->clearCachedQuote($vendorId);
                }
            }
        }
        update_post_meta($orderId, 'sendy_order_collections', $orderCollectNos);
    }

    private function savePdfWaybill($collectno, $base64)
    {
        $uploadsDirectory = $this->getPluginUploadPath();
        $pdfFilePath      = $uploadsDirectory . '/' . $collectno . '.pdf';
        try{
            $f = fopen($pdfFilePath, 'wb');
            fwrite($f, base64_decode($base64));
            fclose($f);
        } catch (Exception $e){
        }
    }

    private function hasTcgShippingMethod($order)
    {
        $result = false;
        if (!empty($order)) {
            $shipping_data = json_decode(get_post_meta($order->get_id(), '_order_shipping_data', true));
            if (is_array($shipping_data)) {
                array_walk(
                    $shipping_data,
                    function ($shippingItem) use (&$result){
                        $shippingInstanceMethod = isset($shippingItem->shipping_method) ? $shippingItem->shipping_method : '';
                        if (strstr($shippingInstanceMethod, 'the_courier_guy')) {
                            $result = true;
                        }
                    }
                );
            }
        }

        return $result;
    }

    /**
     * @param WC_Order $order
     * @param $waybill
     * @param bool $vendorId
     */
    private function updateOrderWaybill(WC_Order $order, $waybill, $vendorId = false)
    {
        if (!empty($order) && !empty($waybill)) {
            $currentWaybill = get_post_meta($order->get_id(), 'dawpro_waybill', true);
            if (!empty($currentWaybill)) {
                $currentWaybill = $currentWaybill . ',';
            }
            update_post_meta($order->get_id(), 'dawpro_waybill', $currentWaybill . $waybill);
            if ($vendorId) {
                update_post_meta($order->get_id(), 'dawpro_waybill_' . $vendorId, $waybill);
            }
        }
    }

    /**
     * @param WC_Order $order
     * @param $collectionNumber
     */
    private function updateOrderCollectionNumber(WC_Order $order, $collectionNumber)
    {
        $currentCollectionNumber = get_post_meta($order->get_id(), 'dawpro_collectno', true);
        if (!empty($currentCollectionNumber)) {
            $currentCollectionNumber = $currentCollectionNumber . ',';
        }
        update_post_meta($order->get_id(), 'dawpro_collectno', $currentCollectionNumber . $collectionNumber);
    }

    /**
     * @param $vendorId
     */
    private function clearCachedQuote($vendorId)
    {
        $vendorId = $vendorId === 0 ? '' : $vendorId;

        if ($wc_session = WC()->session) {
            $wc_session->set(
                'tcg_response' . $vendorId,
                ''
            );
            $wc_session->set(
                'tcg_request' . $vendorId,
                ''

            );
            $wc_session->set(
                'tcg_quote_response' . $vendorId,
                ''
            );
        }
    }

    /**
     * @param $quoteResponse
     * @param $vendorId
     */
    private function updateCachedQuoteResponse($quoteResponse, $vendorId)
    {
        $ts = time();

        if (count($quoteResponse) > 0) {
            $quoteResponse['ts'] = $ts;
        }

        $vendorId = $vendorId === 0 ? '' : $vendorId;

        if ($wc_session = WC()->session) {
            $wc_session->set(
                'tcg_response' . $vendorId,
                json_encode($quoteResponse)
            );
            $wc_session->set(
                'tcg_response' . $vendorId,
                json_encode($quoteResponse)
            );
            $wc_session->set('tcg_quote_response' . $vendorId, $quoteResponse);
        }
    }

    /**
     * @param $vendorId
     * @return mixed
     */
    private function getCachedQuoteResponse($vendorId)
    {
        $vendorId = $vendorId === 0 ? '' : $vendorId;
        if ($wc_session = WC()->session) {
            $response = json_encode(
                $wc_session->get('tcg_quote_response' . $vendorId)
            );
            if ($response != 'null' && strlen($response) > 2) {
                $response = json_decode($response, true);
                $tsnow    = time();
                if (abs($response['ts'] - $tsnow) < 300) {
                    return json_encode($response);
                }
            }
            $r = $wc_session->get('tcg_response');
            if ($r != '') {
                return $wc_session->get('tcg_response');
            }
        }
    }

    /**
     * @param array $quoteParams
     * @param $vendorId - '' if multivendor not enabled
     */
    private function updateCachedQuoteRequest($quoteParams, $vendorId)
    {
        // Current timestamp
        $ts = time();

        $vendorId = $vendorId === 0 ? '' : $vendorId;

        if ($wc_session = WC()->session) {
            $wc_session->set(
                'tcg_quote_request' . $vendorId,
                hash('md5', json_encode($quoteParams) . $vendorId) . '||' . $ts
            );
            $wc_session->set(
                'tcg_request' . $vendorId,
                hash(
                    'md5',
                    json_encode($quoteParams) . $vendorId . $ts
                )
            );
        }
    }

    /**
     * @param $quoteParams
     *
     * @param $vendorId
     * @return bool
     */
    private function compareCachedQuoteRequest($quoteParams, $vendorId)
    {
        $result = false;

        $tsnow = time();
        $hash  = '';

        if ($wc_session = WC()->session) {
            $vendorId = $vendorId === 0 ? '' : $vendorId;

            $request = $wc_session->get('tcg_quote_request' . $vendorId);
            if ($request != 'null' && strlen($request) > 2) {
                $parts = explode('||', $request);
                if (is_array($parts) && count($parts) === 2) {
                    $ts   = $parts[1];
                    $hash = $parts[0];
                }
                $compareQuoteHash = hash(
                    'md5',
                    json_encode(
                        $quoteParams,
                        true
                    ) . $vendorId
                );
                if ($compareQuoteHash === $hash && abs($tsnow - $ts) < 300) {
                    $result = true;
                } else {
                    $wc_session->set('tcg_quote_request' . $vendorId, '');
                }
            } elseif ($wc_session) {
                $cachedQuoteHash = $wc_session->get(
                    'tcg_request' . $vendorId
                );
                if (isset($cachedQuoteHash)) {
                    $parts = explode('||', $cachedQuoteHash);
                    if (is_array($parts) && count($parts) == 2) {
                        $ts   = $parts[1];
                        $hash = $parts[0];
                    }
                }
                $compareQuoteHash = hash(
                    'md5',
                    json_encode($quoteParams, true) . $vendorId
                );
                if ($compareQuoteHash == $hash && abs($tsnow - $ts) < 300) {
                    $result = true;
                }
            }
        }

        return $result;
    }
}
