<?php

namespace Assist\Pay;

use Assist\Client;
use Assist\Config\Config;
use Assist\Helpers\RequestHelper;
use Assist\Helpers\ResponseHelper;
use Assist\Helpers\SignHelper;
use Assist\Request\Cancel\CancelRequest;
use Assist\Request\Charge\ChargeRequest;
use Assist\Request\CreatePayment\CreatePaymentRequest;
use Assist\Request\OrderState\OrderStateRequest;
use Assist\Request\RecurrentPayment\RecurrentPaymentRequest;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\ArgumentNullException;
use Bitrix\Main\ArgumentOutOfRangeException;
use Bitrix\Main\NotImplementedException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\SystemException;
use Bitrix\Main\Type\DateTime;
use Bitrix\Sale\BusinessValue;
use Bitrix\Sale\Order;
use Bitrix\Sale\Payment;

class ClientHelper
{
    /**
     * @param Payment $payment
     * @return Client
     */
    public static function initClient(Payment $payment): Client
    {
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $login = BusinessValue::get(Helper::PS_OPTION_LOGIN, $consumerName);
        $password = BusinessValue::get(Helper::PS_OPTION_PASSWORD, $consumerName);
        $isTestMode = BusinessValue::get(Helper::PS_OPTION_TEST_MODE, $consumerName) === 'Y';
        $apiUrl = BusinessValue::get(Helper::PS_OPTION_API_URL, $consumerName);
        $testApiUrl = Helper::TEST_API_URL;

        $configParams = [
            'api_url' => $apiUrl,
            'test_api_url' => $testApiUrl,
            'merchant_id' => $merchantId,
            'login' => $login,
            'password' => $password,
            'test_mode' => $isTestMode,
        ];

        $clientConfig = new Config($configParams);
        $client = new Client($clientConfig);

        if (Helper::isLogsEnabled()) {
            $logger = Helper::initLogger();
            $client->setLogger($logger);
        }

        return $client;
    }

    /**
     * @param Payment|null $payment
     * @return CreatePaymentRequest
     * @throws ArgumentException
     * @throws ArgumentNullException
     * @throws NotImplementedException
     * @throws ArgumentOutOfRangeException
     * @throws ObjectPropertyException
     * @throws SystemException
     */
    public static function getCreatePaymentRequest(Payment $payment = null): CreatePaymentRequest
    {
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $order = $payment->getOrder();
        $orderNumber = $order->getField(Helper::ORDER_FIELD_ACCOUNT_NUMBER);
        $orderAmount = number_format($payment->getSum(), 2, '.', '');
        $orderCurrency = $payment->getField(Helper::PAYMENT_FIELD_CURRENCY);
        $isRecurrentPayment = BusinessValue::get(Helper::PS_OPTION_RECURRENT_PAYMENT, $consumerName) === 'Y';
        $isPaymentConfirmationEnabled = BusinessValue::get(Helper::PS_OPTION_ORDER_CONFIRMATION, $consumerName) === 'Y';
        $isCardDataSaveEnabled = BusinessValue::get(Helper::PS_OPTION_SAVE_CARD_DATA, $consumerName) === 'Y';
        $isChequeEnabled = BusinessValue::get(Helper::PS_OPTION_PREPARE_ONLINE_CHEQUE, $consumerName) === 'Y';
        $isSendChequeWithConfirm = BusinessValue::get(Helper::PS_OPTION_SEND_CHEQUE_WITH_CONFIRM, $consumerName) === 'Y';
        $successPaymentUrl = BusinessValue::get(Helper::PS_OPTION_SUCCESS_PAYMENT_URL, $consumerName);
        $errorPaymentUrl = BusinessValue::get(Helper::PS_OPTION_ERROR_PAYMENT_URL, $consumerName);
        $taxationSystem = BusinessValue::get(Helper::PS_OPTION_TAX_SYSTEM, $consumerName);
        $fpMode = BusinessValue::get(Helper::PS_OPTION_PAYMENT_METHOD, $consumerName);
        $propertyCollection = $order->getPropertyCollection();
        $email = $propertyCollection->getUserEmail()?->getValue() ?? '';
        $phone = $propertyCollection->getPhone()?->getValue() ?? '';
        $address = $propertyCollection->getAddress()?->getValue() ?? '';
        $fullName = $propertyCollection->getPayerName()?->getValue() ?? '';
        $nameParts = explode(' ', $fullName);
        $lastname = $nameParts[0] ?? '';
        $firstname = $nameParts[1] ?? '';
        $fathername = $nameParts[2] ?? '';

        $requestParams = [
            RequestHelper::PARAM_MERCHANT_ID => $merchantId,
            RequestHelper::PARAM_ORDER_NUMBER => $orderNumber,
            RequestHelper::PARAM_ORDER_AMOUNT => $orderAmount,
            RequestHelper::PARAM_ORDER_CURRENCY => $orderCurrency,
            RequestHelper::PARAM_CHECKVALUE => self::getCheckValue($payment),
            RequestHelper::PARAM_EMAIL => $email,
            RequestHelper::PARAM_MOBILE_PHONE => $phone,
            RequestHelper::PARAM_ADDRESS => $address,
            RequestHelper::PARAM_FIRSTNAME => $firstname,
            RequestHelper::PARAM_LASTNAME => $lastname,
            RequestHelper::PARAM_MIDDLENAME => $fathername,
            RequestHelper::PARAM_PAYMENT_TYPE_UMONEY => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_UMONEY,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_WEBMONEY => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_WEBMONEY,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_QIWI => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_QIWI,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_SPB => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_SPB,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_GOOGLE_PAY => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_GOOGLE_PAY,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_APPLE_PAY => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_APPLE_PAY,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_SAMSUNG_PAY => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_SAMSUNG_PAY,
                $consumerName
            ),
            RequestHelper::PARAM_PAYMENT_TYPE_MIR_PAY => self::getPaymentSystemValueByName(
                Helper::PS_OPTION_PAYMENT_TYPE_MIR_PAY,
                $consumerName
            ),
        ];

        if ($isChequeEnabled && !$isSendChequeWithConfirm) {
            $requestParams[RequestHelper::PARAM_CHEQUE_ITEMS] = self::getChequeItems($payment);
            $requestParams[RequestHelper::TAXATION_SYSTEM] = $taxationSystem;
            $requestParams[RequestHelper::FP_MODE] = $fpMode;
        }

        if ($isPaymentConfirmationEnabled) {
            $requestParams[RequestHelper::PARAM_DELAY] = Helper::API_PARAM_VALUE_DELAY_ENABLED;
        }

        if ($successPaymentUrl) {
            $requestParams[RequestHelper::PARAM_URL_RETURN_OK] = $successPaymentUrl;
        }

        if ($errorPaymentUrl) {
            $requestParams[RequestHelper::PARAM_URL_RETURN_NO] = $errorPaymentUrl;
        }

        if ($isCardDataSaveEnabled) {
            $order = Order::loadByAccountNumber($orderNumber);
            $userId = $order->getUserId();

            $requestParams[RequestHelper::PARAM_CUSTOMER_NUMBER] = $userId;
        }

        if ($isRecurrentPayment) {
            $recurrentSettings = json_decode($payment->getField(Helper::PAYMENT_FIELD_STATUS_MESSAGE), true);
            $period = $recurrentSettings['recurrent_period'] ?? Helper::DEFAULT_RECURRENT_PERIOD;
            $maxDateFromCurrentDate = $recurrentSettings['recurrent_max_date_from_current_day'] ?? Helper::DEFAULT_RECURRENT_MAX_DATE_FROM_CURRENT_DAY;
            $date = new DateTime();
            $date->add($maxDateFromCurrentDate);
            $date = $date->format('d.m.Y');

            $requestParams[RequestHelper::PARAM_RECURRING_INDICATOR] = Helper::API_PARAM_VALUE_RECURRENT_PAYMENT_ENABLED;
            $requestParams[RequestHelper::PARAM_RECURRING_MIN_AMOUNT] = $orderAmount;
            $requestParams[RequestHelper::PARAM_RECURRING_MAX_AMOUNT] = $orderAmount;
            $requestParams[RequestHelper::PARAM_RECURRING_PERIOD] = $period;
            $requestParams[RequestHelper::PARAM_RECURRING_MAX_DATE] = $date;
        }

        if (Helper::isLogsEnabled()) {
            $logger = Helper::initLogger();
            $logger->debug('CreatePaymentRequest params');
            $logger->debug(json_encode($requestParams));
        }

        return new CreatePaymentRequest($requestParams);
    }

    /**
     * @param Payment $payment
     * @return ChargeRequest
     * @throws ArgumentException
     * @throws ArgumentNullException
     */
    public static function getConfirmRequest(Payment $payment): ChargeRequest
    {
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $login = BusinessValue::get(Helper::PS_OPTION_LOGIN, $consumerName);
        $password = BusinessValue::get(Helper::PS_OPTION_PASSWORD, $consumerName);
        $isChequeEnabled = BusinessValue::get(Helper::PS_OPTION_PREPARE_ONLINE_CHEQUE, $consumerName) === 'Y';
        $isSendChequeWithConfirm = BusinessValue::get(Helper::PS_OPTION_SEND_CHEQUE_WITH_CONFIRM, $consumerName) === 'Y';
        $taxationSystem = BusinessValue::get(Helper::PS_OPTION_TAX_SYSTEM, $consumerName);
        $fpMode = BusinessValue::get(Helper::PS_OPTION_PAYMENT_METHOD, $consumerName);
        $billNumber = $payment->getField(Helper::PAYMENT_FIELD_PS_INVOICE_ID);
        $requestParams = [
            RequestHelper::PARAM_MERCHANT_ID => $merchantId,
            RequestHelper::PARAM_BILL_NUMBER => $billNumber,
            RequestHelper::PARAM_LOGIN => $login,
            RequestHelper::PARAM_PASSWORD => $password,
        ];

        if ($isChequeEnabled && $isSendChequeWithConfirm) {
            $requestParams[RequestHelper::PARAM_CHEQUE_ITEMS] = self::getChequeItems($payment);
            $requestParams[RequestHelper::TAXATION_SYSTEM] = $taxationSystem;
            $requestParams[RequestHelper::FP_MODE] = $fpMode;
        }

        if (Helper::isLogsEnabled()) {
            $requestParamsForLog = $requestParams;
            unset($requestParamsForLog[RequestHelper::PARAM_PASSWORD]);
            $logger = Helper::initLogger();
            $logger->debug('ChargeRequest params');
            $logger->debug(json_encode($requestParamsForLog));
        }

        return new ChargeRequest($requestParams);
    }

    /**
     * @param Payment $payment
     * @return OrderStateRequest
     */
    public static function getStatusRequest(Payment $payment): OrderStateRequest
    {
        $orderId = $payment->getOrder()->getField(Helper::ORDER_FIELD_ACCOUNT_NUMBER);;
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $login = BusinessValue::get(Helper::PS_OPTION_LOGIN, $consumerName);
        $password = BusinessValue::get(Helper::PS_OPTION_PASSWORD, $consumerName);
        $requestParams = [
            RequestHelper::PARAM_MERCHANT_ID => $merchantId,
            RequestHelper::PARAM_ORDER_NUMBER => $orderId,
            RequestHelper::PARAM_LOGIN => $login,
            RequestHelper::PARAM_PASSWORD => $password,
        ];

        if (Helper::isLogsEnabled()) {
            $requestParamsForLog = $requestParams;
            unset($requestParamsForLog[RequestHelper::PARAM_PASSWORD]);
            $logger = Helper::initLogger();
            $logger->debug('ChargeRequest params');
            $logger->debug(json_encode($requestParamsForLog));
        }

        return new OrderStateRequest($requestParams);
    }

    /**
     * @param Payment $payment
     * @return RecurrentPaymentRequest
     */
    public static function getRecurrentPaymentRequest(Payment $payment): RecurrentPaymentRequest
    {
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $login = BusinessValue::get(Helper::PS_OPTION_LOGIN, $consumerName);
        $password = BusinessValue::get(Helper::PS_OPTION_PASSWORD, $consumerName);
        $orderNumber = $payment->getOrder()->getField(Helper::ORDER_FIELD_ACCOUNT_NUMBER);
        $billNumber = $payment->getField(Helper::PAYMENT_FIELD_PS_INVOICE_ID);
        $amount = number_format($payment->getSum(), 2, '.', '');
        $currency = $payment->getField(Helper::PAYMENT_FIELD_CURRENCY);

        $requestParams = [
            RequestHelper::PARAM_MERCHANT_ID => $merchantId,
            RequestHelper::PARAM_ORDER_NUMBER => $orderNumber,
            RequestHelper::PARAM_BILL_NUMBER => $billNumber,
            RequestHelper::PARAM_LOGIN => $login,
            RequestHelper::PARAM_PASSWORD => $password,
            RequestHelper::PARAM_AMOUNT => $amount,
            RequestHelper::PARAM_CURRENCY => $currency,
        ];

        if (Helper::isLogsEnabled()) {
            $requestParamsForLog = $requestParams;
            unset($requestParamsForLog[RequestHelper::PARAM_PASSWORD]);
            $logger = Helper::initLogger();
            $logger->debug('ChargeRequest params');
            $logger->debug(json_encode($requestParamsForLog));
        }

        return new RecurrentPaymentRequest($requestParams);
    }


    /**
     * @param Payment $payment
     * @return CancelRequest
     */
    public static function getCancelPaymentRequest(Payment $payment): CancelRequest
    {
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $login = BusinessValue::get(Helper::PS_OPTION_LOGIN, $consumerName);
        $password = BusinessValue::get(Helper::PS_OPTION_PASSWORD, $consumerName);
        $billNumber = $payment->getField('PS_INVOICE_ID');
        $requestParams = [
            RequestHelper::PARAM_MERCHANT_ID => $merchantId,
            RequestHelper::PARAM_BILL_NUMBER => $billNumber,
            RequestHelper::PARAM_LOGIN => $login,
            RequestHelper::PARAM_PASSWORD => $password,
        ];

        if (Helper::isLogsEnabled()) {
            $requestParamsForLog = $requestParams;
            unset($requestParamsForLog[RequestHelper::PARAM_PASSWORD]);
            $logger = Helper::initLogger();
            $logger->debug('ChargeRequest params');
            $logger->debug(json_encode($requestParamsForLog));
        }

        return new CancelRequest($requestParams);
    }

    /**
     * @param Payment $payment
     * @return string
     * @throws ArgumentException
     * @throws ArgumentNullException
     */
    private static function getChequeItems(Payment $payment): string
    {
        $chequeItems = [];
        $basket = $payment->getOrder()->getBasket();
        $paySystem = $payment->getPaySystem();
        $taxSetting = BusinessValue::get(Helper::PS_OPTION_TAX_RATE, $paySystem->getConsumerName());
        $useTaxFromProduct = BusinessValue::get(Helper::PS_OPTION_TAX_RATE_FROM_PRODUCT_SETTINGS, $paySystem->getConsumerName());
        $subjtype = BusinessValue::get(Helper::PS_OPTION_SUBJECT_FEATURE, $paySystem->getConsumerName());

        foreach ($basket as $item) {
            $bitrixVatRate = false;

            if ($useTaxFromProduct === 'Y') {
                $productId = $item->getProductId();
                $vatProps = \CCatalogProduct::GetVATDataByID($productId);
                $bitrixVatRate = $vatProps ? Helper::getTaxRateFor($vatProps['RATE']) : false;
            }

            $tax = $bitrixVatRate ?: $taxSetting;

            $chequeItems[] = [
                'id' => $item->getProductId(),
                'name' => $item->getField(Helper::BASKET_FIELD_ITEM_NAME),
                'quantity' => $item->getQuantity(),
                'price' => $item->getPrice(),
                'amount' => $item->getFinalPrice(),
                'tax' => $tax,
                'subjtype' => $subjtype
            ];
        }

        $shipmentItems = $payment->getOrder()->getShipmentCollection();

        foreach ($shipmentItems as $item) {
            $deliveryName = $item->getDeliveryName();

            if (!$item->isSystem() && !self::isDeliveryDisabledForCheque($deliveryName, $item->getPrice())) {
                $chequeItems[] = [
                    'id' => $item->getDeliveryId(),
                    'name' => $item->getDeliveryName(),
                    'quantity' => 1,
                    'price' => $item->getPrice(),
                    'amount' => $item->getPrice(),
                    'tax' => $taxSetting,
                    'subjtype' => $subjtype
                ];
            }
        }

        return json_encode(['items' => $chequeItems]);
    }

    /**
     * @param Payment $payment
     * @param bool $isResultResponse
     * @param string|null $status
     * @return string
     */
    public static function getCheckValue(
        Payment $payment,
        bool $isResultResponse = false,
        string $status = null
    ): string {
        $paySystem = $payment->getPaySystem();
        $consumerName = $paySystem->getConsumerName();
        $secret = BusinessValue::get(Helper::PS_OPTION_SECRET, $consumerName) ?? '';
        $merchantId = BusinessValue::get(Helper::PS_OPTION_MERCHANT_ID, $consumerName);
        $orderNumber = $payment->getOrder()->getField(Helper::ORDER_FIELD_ACCOUNT_NUMBER);
        $amount = number_format($payment->getSum(), 2, '.', '');
        $currency = $payment->getField(Helper::PAYMENT_FIELD_CURRENCY);

        $params = [
            RequestHelper::PARAM_MERCHANT_ID => $merchantId,
            RequestHelper::PARAM_ORDER_NUMBER => $orderNumber,
            RequestHelper::PARAM_ORDER_AMOUNT => $amount,
            RequestHelper::PARAM_ORDER_CURRENCY => $currency,
        ];

        if ($isResultResponse) {
            $params[ResponseHelper::ORDER_STATE] = $status;

            return SignHelper::getStatusResponseCheckValue($params, $secret);
        }

        return SignHelper::getCheckValue($params, $secret);
    }

    /**
     * @param string $name
     * @param string $consumerName
     * @return int
     */
    public static function getPaymentSystemValueByName(string $name, string $consumerName): int
    {
        return BusinessValue::get($name, $consumerName) === 'Y' ? 1 : 0;
    }

    /**
     * @param string $shipmentSystemName
     * @param int $price
     * @return bool
     */
    private static function isDeliveryDisabledForCheque(string $shipmentSystemName, int $price): bool
    {
        return $shipmentSystemName === Helper::DELIVERY_WITHOUT_DELIVERY_NAME
            || ($shipmentSystemName === Helper::DELIVERY_SELF_DELIVERY_NAME && $price === 0);
    }
}
