<?php

namespace KupShop\CeskaPostaBundle;

use Psr\Log\LoggerInterface;

class CeskaPosta
{
    protected $idContract;
    protected $customerID;
    protected $postCode;
    protected $locationNumber;
    protected $idExtTransaction;

    public $ns = 'https://b2b.postaonline.cz/schema/B2BCommon-v1';
    public $ns2 = 'https://b2b.postaonline.cz/schema/POLServices-v1';

    public $request_url;
    public $request;
    public $response;
    public $error;
    protected $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
        $this->idContract = findModule(\Modules::CESKA_POSTA, 'idContract');
        $this->customerID = findModule(\Modules::CESKA_POSTA, 'customerID');
        $this->postCode = findModule(\Modules::CESKA_POSTA, 'postCode');
        $this->locationNumber = findModule(\Modules::CESKA_POSTA, 'locationNumber');
    }

    public function configure($idContract, $customerID, $postCode, $locationNumber)
    {
        $this->idContract = $idContract;
        $this->customerID = $customerID;
        $this->postCode = $postCode;
        $this->locationNumber = $locationNumber;
    }

    public function getHeader($type = '')
    {
        $this->idExtTransaction++;
        $header = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<b2bRequest xmlns="'.$this->ns.'" xmlns:ns2="'.$this->ns2.'">
	<header>
		<idExtTransaction>'.$this->idExtTransaction.'</idExtTransaction>
		<timeStamp>'.date('Y-m-d\TH:i:s.vP').'</timeStamp>
		<idContract>'.$this->idContract.'</idContract>
	</header>
	';
        if ($type == 'getParcelsPrinting') {
            $header .= '<serviceData>
        <ns2:getParcelsPrinting>
			<ns2:doPrintingHeader>
				<ns2:customerID>'.$this->customerID.'</ns2:customerID>
				<ns2:contractNumber>'.$this->idContract.'</ns2:contractNumber>
				<ns2:shiftHorizontal>0</ns2:shiftHorizontal>
				<ns2:shiftVertical>0</ns2:shiftVertical>
			</ns2:doPrintingHeader>
			<ns2:doPrintingData></ns2:doPrintingData>
		</ns2:getParcelsPrinting>
   </serviceData>
';
        }
        if ($type == 'sendParcels') {
            $header .= '<serviceData>
        <ns2:sendParcels>
            <ns2:doParcelHeader>
                <ns2:transmissionDate>'.date('d.m.Y').'</ns2:transmissionDate>
                <ns2:customerID>'.$this->customerID.'</ns2:customerID>
                <ns2:postCode>'.$this->postCode.'</ns2:postCode>
                <ns2:locationNumber>'.$this->locationNumber.'</ns2:locationNumber>
            </ns2:doParcelHeader>
            <ns2:doParcelData>
                <ns2:doParcelParams></ns2:doParcelParams>
                <ns2:doParcelAddress></ns2:doParcelAddress>
            </ns2:doParcelData>
        </ns2:sendParcels>
    </serviceData>
';
        }

        $header .= '</b2bRequest>';
        $simple_xml = simplexml_load_string($header);

        return $simple_xml;
    }

    public function get_b2bRequest($order, $type = 'sendParcels')
    {
        $b2bRequest = $this->getHeader($type);
        $serviceData = $b2bRequest->serviceData;

        $doParcelParams = $serviceData->children('ns2', true)->sendParcels->doParcelData->doParcelParams;
        $doParcelParams->addChild('ns2:recordID', $order->id, $this->ns2);
        $doParcelParams->addChild('ns2:currency', $order->currency, $this->ns2);

        $delivery_address = !empty($order->delivery_street); // true - use delivery address, false - use invoice address

        // surname = name surname firm
        // surname - string50
        $surname = ($delivery_address ? $order->delivery_name : $order->invoice_name).' ';
        $surname .= $delivery_address ? $order->delivery_surname.(!empty($order->delivery_firm) ? ' '.$order->delivery_firm : '') : $order->invoice_surname;

        // street - string40
        $street = $delivery_address ? $order->delivery_street : $order->invoice_street;

        // firstName = custom_address (Address line 2 / Upřesnění adresy), if exists
        // firstName - string50
        $custom_address = ($delivery_address && !empty($order->delivery_custom_address)) ? ', '.$order->delivery_custom_address : (!$delivery_address && !empty($order->invoice_custom_address) ? ', '.$order->invoice_custom_address : '');

        // city = city, state
        // city - string40
        $city = $delivery_address ? $order->delivery_city : $order->invoice_city;
        $city .= ($delivery_address && !empty($order->delivery_state)) ? ', '.$order->delivery_state : (!$delivery_address && !empty($order->invoice_state) ? ', '.$order->invoice_state : '');

        // zipCode - string25
        $zip = $delivery_address ? $order->delivery_zip : $order->invoice_zip;

        $country = $delivery_address ? $order->delivery_country : $order->invoice_country;

        if ($transliterator = \Transliterator::create('Russian-Latin/BGN')) {
            $surname = $transliterator->transliterate($surname);
            $street = $transliterator->transliterate($street);
            $custom_address = $transliterator->transliterate($custom_address);
            $city = $transliterator->transliterate($city);
        }

        if (mb_strlen($surname) > 50) {
            $surname = mb_substr($surname, 0, 50);
        }
        if (mb_strlen($city) > 40) {
            $city = mb_substr($city, 0, 40);
        }

        $doParcelAddress = $serviceData->children('ns2', true)->sendParcels->doParcelData->doParcelAddress;
        $doParcelAddress->addChild('ns2:surname', '', $this->ns2)[0] = trim($surname);
        if (!empty($custom_address)) {
            $doParcelAddress->addChild('ns2:firstName', '', $this->ns2)[0] = trim($custom_address);
        }
        $doParcelAddress->addChild('ns2:subject', 'F', $this->ns2);
        $doParcelAddress->addChild('ns2:street', '', $this->ns2)[0] = trim($street);
        $doParcelAddress->addChild('ns2:city', '', $this->ns2)[0] = trim($city);
        $doParcelAddress->addChild('ns2:zipCode', '', $this->ns2)[0] = trim($zip);
        $doParcelAddress->addChild('ns2:isoCountry', trim($country), $this->ns2);
        if (in_array($country, ['US', 'MX', 'AU', 'BR', 'CA', 'ES'])) {
            $state = ($delivery_address && !empty($order->delivery_state)) ? $order->delivery_state : $order->invoice_state;
            $doParcelAddress->addChild('ns2:subIsoCountry', trim($state), $this->ns2);
        }
        $doParcelAddress->addChild('ns2:emailAddress', $order->invoice_email, $this->ns2);
        $phone = trim($order->invoice_phone);
        if (mb_strlen($phone) > 13) {
            $phone = mb_substr($phone, mb_strlen($phone) - 13);
        }
        $doParcelAddress->addChild('ns2:mobileNumber', '', $this->ns2)[0] = trim($phone);

        return $b2bRequest;
    }

    public function sendParcels($b2bRequest)
    {
        $this->request_url = 'https://b2b.postaonline.cz/services/POLService/v1/sendParcels';

        $this->request = $b2bRequest->asXML();

        if (!$this->sendToCeskaPosta($this->request, $this->request_url)) {
            return false;
        }
        $parser = xml_parser_create();
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
        if (!xml_parse_into_struct($parser, $this->response, $values, $index)) {
            $this->error = 'xml_parse error: '.xml_error_string(xml_get_error_code($parser)).' at line '.xml_get_current_line_number($parser);
            xml_parser_free($parser);

            return false;
        }
        xml_parser_free($parser);
        if (array_key_exists('v1:B2BFaultMessage', $index)) {
            $i = $index['v1:errorDetail'][0];
            $v = $values[$i]['value'];
            $this->error = 'Error! B2BFaultMessage: '.$v;

            return false;
        }
        if (!array_key_exists('v1:b2bASyncResponse', $index)) {
            $this->error = 'Error! No b2bASyncResponse!';

            return false;
        }
        if (!array_key_exists('v1:idTransaction', $index)) {
            $this->error = 'Error! No idTransaction!';

            return false;
        }
        $i = $index['v1:idTransaction'][0];
        $idTransaction = $values[$i]['value'];

        return $idTransaction;
    }

    public function getResultParcels($idTransaction)
    {
        $this->request_url = 'https://b2b.postaonline.cz/services/POLService/v1/getResultParcels';

        $b2bRequest = $this->getHeader('getResultParcels');
        $b2bRequest->addChild('idTransaction', $idTransaction);
        $this->request = $b2bRequest->asXML();

        if (!$this->sendToCeskaPosta($this->request, $this->request_url)) {
            return false;
        }
        $parser = xml_parser_create();
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
        if (!xml_parse_into_struct($parser, $this->response, $values, $index)) {
            $this->error = 'xml_parse error: '.xml_error_string(xml_get_error_code($parser)).' at line '.xml_get_current_line_number($parser);
            xml_parser_free($parser);

            return false;
        }
        xml_parser_free($parser);
        if (array_key_exists('v1:B2BFaultMessage', $index)) {
            $i = $index['v1:errorDetail'][0];
            $v = $values[$i]['value'];
            $this->error = 'Error! B2BFaultMessage: '.$v;
            $i = $index['v1:errorCode'][0];
            $errorCode = $values[$i]['value'];
            if ($errorCode == 10) {
                // 10 = UNFINISHED_PROCESS = Zpracování není ještě ukončeno
                sleep(1);

                return $this->getResultParcels($idTransaction);
            }

            return false;
        }
        if (!array_key_exists('v1_1:responseCode', $index)) {
            $this->error = 'Error! No responseCode!';

            return false;
        }
        $i = $index['v1_1:responseCode'][0];
        $responseCode = $values[$i]['value'];
        if ($responseCode != '1') {
            $this->error = 'Error! ';
            foreach ($index['v1_1:responseCode'] as $key => $i) {
                $responseCode = $values[$i]['value'];
                $i = $index['v1_1:responseText'][$key];
                $v = $values[$i]['value'];
                $this->error .= $responseCode.': '.$v.'; ';
            }

            // ignore error
            // 09.03.2018:
            // getResultParcels vrati Error! 19: BATCH_INVALID, i kdyz je vsechno OK,
            // a vrati take parcelCode
            return false;
        }
        if (!array_key_exists('v1_1:parcelCode', $index)) {
            $this->error .= 'Error! No parcelCode!';

            return false;
        }
        $parcelCode = [];
        foreach ($index['v1_1:parcelCode'] as $i) {
            $parcelCode[] = $values[$i]['value'];
        }

        return array_unique($parcelCode);
    }

    public function getParcelsPrinting($parcelCode, $idForm, $print_position = null)
    {
        $this->request_url = 'https://b2b.postaonline.cz/services/POLService/v1/getParcelsPrinting';

        $b2bRequest = $this->getHeader('getParcelsPrinting');
        $serviceData = $b2bRequest->serviceData;
        $doPrintingHeader = $serviceData->children('ns2', true)->getParcelsPrinting->doPrintingHeader;
        $doPrintingHeader->addChild('ns2:idForm', $idForm, $this->ns2);
        if ($print_position) {
            $doPrintingHeader->addChild('ns2:position', $print_position, $this->ns2);
        }
        $doPrintingData = $serviceData->children('ns2', true)->getParcelsPrinting->doPrintingData;
        foreach ($parcelCode as $code) {
            $doPrintingData->addChild('ns2:parcelCode', $code, $this->ns2);
        }

        $this->request = $b2bRequest->asXML();

        if (!$this->sendToCeskaPosta($this->request, $this->request_url)) {
            return false;
        }
        $parser = xml_parser_create();
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
        if (!xml_parse_into_struct($parser, $this->response, $values, $index)) {
            $this->error = 'xml_parse error: '.xml_error_string(xml_get_error_code($parser)).' at line '.xml_get_current_line_number($parser);
            xml_parser_free($parser);

            return false;
        }
        xml_parser_free($parser);
        if (array_key_exists('v1:B2BFaultMessage', $index)) {
            $i = $index['v1:errorDetail'][0];
            $v = $values[$i]['value'];
            $this->error = 'Error! B2BFaultMessage: '.$v;

            return false;
        }
        if (!array_key_exists('v1_1:responseCode', $index)) {
            $this->error = 'Error! No responseCode!';

            return false;
        }
        $i = $index['v1_1:responseCode'][0];
        $responseCode = $values[$i]['value'];
        if ($responseCode != '1') {
            $i = $index['v1_1:responseText'][0];
            $v = $values[$i]['value'];
            $this->error = 'Error! '.$responseCode.': '.$v;

            return false;
        }
        if (!array_key_exists('v1_1:file', $index)) {
            $this->error = 'Error! No file!';

            return false;
        }
        $i = $index['v1_1:file'][0];
        $f = $values[$i]['value'];

        $pdf = base64_decode($f);

        return $pdf;
    }

    public function sendToCeskaPosta($request = '', $url = null)
    {
        $this->logger->notice('Ceska Posta Podani Online', ['request_url' => $url, 'request' => $request]);
        $curl = $this->createCurl($request, $url);
        $this->response = curl_exec($curl);
        if ($this->response === false) {
            $this->error = 'curl_error: '.curl_error($curl);
            $this->logger->notice('Ceska Posta Podani Online', ['error' => $this->error]);

            return false;
        } else {
            $this->error = '';
            $this->logger->notice('Ceska Posta Podani Online', ['response' => $this->response]);

            return true;
        }
    }

    public function createCurl($encodedBody, $url = null)
    {
        if (empty($url)) {
            $url = $this->request_url;
        }

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        //        curl_setopt($curl, CURLOPT_VERBOSE, true);
        //        curl_setopt($curl, CURLOPT_CERTINFO, true);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($curl, CURLOPT_CAINFO, __DIR__.DIRECTORY_SEPARATOR.'Resources/Certificates/postsignum_vca4.pem');

        curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'P12');
        curl_setopt($curl, CURLOPT_SSLCERT, __DIR__.DIRECTORY_SEPARATOR.'Resources/Certificates/post_2024.p12');
        curl_setopt($curl, CURLOPT_SSLCERTPASSWD, 'wpj');

        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($curl, CURLOPT_POSTFIELDS, $encodedBody);

        return $curl;
    }
}
