<?php
/**
 * Credorax Payments For WooCommerce
 * https://www.credorax.com/
 *
 * @category Credorax
 * @package  WC_Credorax
 * @author   Girit-Interactive (https://www.girit-tech.com/)
 */

 if (! defined('ABSPATH')) {
     exit; // Exit if accessed directly.
 }

/**
 * Credorax Credorax abstract request model.
 */
abstract class WC_Credorax_Model_AbstractRequest extends WC_Credorax_Model_AbstractApi
{

    /**
     * @var array
     */
    protected $_curlResponse = [];

    /**
     * @var WC_Credorax_Model_Response_Factory
     */
    protected $_responseFactory;

    /**
     * @method __construct
     */
    public function __construct()
    {
        parent::__construct();
        $this->_responseFactory = new WC_Credorax_Model_Response_Factory;
    }

    /**
     * @return AbstractResponse
     * @throws \Exception
     * @throws Exception
     */
    public function process()
    {
        $this->sendRequest();

        return $this
            ->getResponseHandler()
            ->process();
    }

    /**
     * Return full endpoint to particular method for request call.
     *
     * @return string
     */
    abstract protected function getEndpoint();

    /**
     * Return method for request call.
     *
     * @return string
     */
    abstract protected function getRequestMethod();

    /**
     * Return response handler type.
     *
     * @return string
     */
    abstract protected function getResponseHandlerType();

    /**
     * Return request params.
     *
     * @return array
     */
    protected function getParams()
    {
        $params =  [
            'M' => WC_Credorax_Model_Config::getMerchantId()
        ];

		$sub_merchant_id = WC_Credorax_Model_Config::getSubMerchantId();
		if ( ! empty( $sub_merchant_id ) ) {
			$params['h3'] = $sub_merchant_id;
		}

        if (($billingDescriptor = WC_Credorax_Model_Config::getBillingDescriptor())) {
            $params['i2'] = $billingDescriptor;
        }

        return $params;
    }

    /**
     * @return array
     * @throws Exception
     */
    protected function prepareParams()
    {
        $params = array_filter($this->getParams(), function ($value) {
            return !is_null($value) && $value !== '';
        });

        foreach ($params as &$val) {
            $val = $this->fixUTF8($val);
        }

        //= Add the K param (SignatureKey|SHA256 Cipher)
        ksort($params);
        $cipherParams = preg_replace("/[\<|\>|\"|\'|\\|\(|\)]/", " ", $params);
        $cipherParams = array_map('trim', $cipherParams);
        $cipherString = implode("", $cipherParams);
        $cipherString .= WC_Credorax_Model_Config::getSignatureKey();
        $params['K'] = hash('sha256', $cipherString);

        return $params;
    }

    /**
     * @return AbstractRequest
     * @throws \Exception
     */
    protected function sendRequest()
    {
        $endpoint = $this->getEndpoint();
        $params = $this->prepareParams();

        $reqParams = http_build_query($params);
        $reqHeaders = [
            'Content-type' => 'application/x-www-form-urlencoded; charset=utf-8',
            'Content-Length' => \strlen($reqParams)
        ];
        $userAgent = isset($_SERVER['HTTP_USER_AGENT'])? $_SERVER['HTTP_USER_AGENT'] : null;
        if ($userAgent) {
            $reqHeaders['User-Agent'] = $userAgent;
        }

        WC_Credorax_Model_Config::log('WC_Credorax_Model_Request_AbstractPayment::sendRequest() ', 'debug', [
            'method' => $this->getRequestMethod(),
            'request' => [
                'Type' => 'POST',
                'Endpoint' => $endpoint,
                'headers' => $reqHeaders,
                'Params' => $params,
            ],
        ]);

        //== Send CURL post ==//
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $endpoint,
            CURLOPT_POSTFIELDS => $reqParams,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_POST => 1,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYPEER => 1,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_FORBID_REUSE => 1,
            CURLOPT_HTTPHEADER => $reqHeaders,
        ]);
        $resHeaders = [];
        curl_setopt(
            $ch,
            CURLOPT_HEADERFUNCTION,
            function ($curl, $header) use (&$resHeaders) {
                $len = strlen($header);
                $header = explode(':', $header, 2);
                if (count($header) < 2) {
                    return $len;
                }
                $key = trim($header[0]);
                $val = trim($header[1]);
                if (isset($resHeaders[$key])) {
                    $resHeaders[$key] = (array)$resHeaders[$key];
                    $resHeaders[$key][] = $val;
                } else {
                    $resHeaders[$key] = $val;
                }

                return $len;
            }
        );

        $resBody = curl_exec($ch);
        $resHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);
        //====================================================================//

        if ($err) {
            $this->_curlResponse = [
                'response_handler_type' => $this->getResponseHandlerType(),
                'status' => $resHttpCode,
                'headers' => $resHeaders,
                'body' => $resBody,
                'error' => $err
            ];
        } else {
            $this->_curlResponse = [
                'response_handler_type' => $this->getResponseHandlerType(),
                'status' => $resHttpCode,
                'headers' => $resHeaders,
                'body' => $resBody,
                'error' => false
            ];
        }

        return $this;
    }

    /**
     * Return proper response handler.
     *
     * @return WC_Credorax_Model_ResponseInterface
     * @throws \Exception
     */
    protected function getResponseHandler()
    {
        $responseHandler = $this->_responseFactory->create(
            $this->getResponseHandlerType(),
            $this->_curlResponse
        );

        return $responseHandler;
    }

    /**
     * @param WC_Order $order
     *
     * @return array
     */
    protected function getOrderData(WC_Order $order)
    {
        $orderData = [
            'a1' => $order->get_id() . (int)round(microtime(true) * 10000),
            'h9' => $order-> get_id(),
            'a4' => $this->amountFormat($order->get_total(), $order->get_currency()),
            'a5' => $order->get_currency(),
            'a6' => date('ymd', strtotime($order->get_date_created())),
            'a7' => date('His', strtotime($order->get_date_created())),
        ];

        return $orderData;
    }

    /**
     * @param WC_Order $order
     *
     * @return array
     */
    protected function getBillingData(WC_Order $order)
    {
        $data = [
            'b2' => $this->getCcTypeNumberByCode($order->get_meta(WC_Credorax_Gateway::KEY_CC_TYPE)),
            'c1' => $this->fixUTF8($order->get_meta(WC_Credorax_Gateway::KEY_CC_OWNER)),
            'c2' => (int) substr(preg_replace('/[^\d]/', '', $order->get_billing_phone()), 0, 15),
            'c3' => $this->fixUTF8($order->get_billing_email()),
            'c5' => preg_replace('/(?i)[^\d\p{L}\p{C}À-ÿ_]/', '-', $this->fixUTF8($order->get_billing_address_1() . ' ' . $order->get_billing_address_2())),
            'c7' => preg_replace('/(?i)[^\p{L}\p{C}À-ÿ_]/', '-', $this->fixUTF8($order->get_billing_city())),
            'c9' => $order->get_billing_country(),
            'c10' => preg_replace('/[\W_]/', '', $this->fixUTF8($order->get_billing_postcode())),
        ];
        return $data;
    }
}
