<?php

declare(strict_types=1);

use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Symfony\Component\Routing\Router;

require_once 'class.PayPal.php';

/**
 * Dependencies: `composer require paypal/rest-api-sdk-php=^1.14`
 * Example config: $cfg['Modules']['payments']['PayPal'] = [
 *     'clientID' => 'client id',
 *     'secret' => 'client secret',
 *  webProfileID - create temporary (3 hours): symfony kupshop:paypal-create-web-profile
 *  or permanent profile: symfony kupshop:paypal-create-web-profile --permanent
 *  optional image path as an argument: symfony kupshop:paypal-create-web-profile --permanent templates/images/logo.png
 *     'webProfileID' => 'web profile id',
 *     'mode' => 'sandbox' OR 'live',
 *     'enableLog' => false,
 *   ].
 */
class PayPalPlus extends PayPal
{
    public static $name = 'PayPal Plus platební brána';

    protected $templateCart = 'payment.PayPalPlus.cart.tpl';
    protected $templateOrderView = 'payment.PayPalPlus.orderView.tpl';
    protected $templateInit = 'payment.PayPalPlus.init.tpl';

    public $class = 'PayPalPlus';

    /** Vytáhne existující, nebo vytvoří novou platbu */
    public function getPayment()
    {
        $cart = ServiceContainer::getService(\KupShop\OrderingBundle\Cart::class);
        // Preferuj ID z objednavky
        if (isset($this->order) && ($this->order->getData('paypalplus')['id'] ?? false)) {
            $payment = $this->getPayPalPayment($this->getApiContext(), $this->order->getData('paypalplus')['id']);
        } elseif ($cart->getData('paypalplus') && $cart->getData('paypalplus')['id']) {
            $payment = $this->getPayPalPayment($this->getApiContext(), $cart->getData('paypalplus')['id']);
        } else {
            $payment = $this->createPayPalPayment();
            $cart->setData('paypalplus', ['id' => $payment->getId(), 'token' => $payment->getToken()]);
        }

        return $payment;
    }

    public function getPaymentUrl()
    {
        if (empty($this->order)) {
            throw new \KupShop\OrderingBundle\Exception\PaymentException(translate('sameDevicePayment', 'payment'));
        }

        return path('kupshop_content_orders_order',
            ['id' => $this->order->id, 'cf' => $this->order->getSecurityCode(), 'status' => 1, 'immediate_pay' => 1]);
    }

    /** Pošle patch request pokud není platba aktualizovana - nema security code */
    public function makeSurePaymentIsUpdated()
    {
        $payment = $this->getPayment();
        if (empty($payment->getTransactions()[0]->getCustom())) {
            return $this->updatePayment();
        }

        return $payment;
    }

    /** Vytvoří payment s dummy daty - protože se musí tvořit už v košíku */
    protected function createPayPalPayment($useOriginalCurrency = true, $totalPrice = 0): PayPal\Api\Payment
    {
        $apiContext = $this->getApiContext();
        $dbcfg = Settings::getDefault();
        if ($this->order && $remainingPayment = roundPrice($this->order->getRemainingPayment())->asFloat() <= 0) {
            $this->success(translate('paymentSuccess', 'payment'));
        }

        $payer = new \PayPal\Api\Payer();
        $payer->setPaymentMethod('paypal');

        $amount = new \PayPal\Api\Amount();
        $amount->setCurrency('EUR')->setTotal('2.00');

        $item1 = new \PayPal\Api\Item();
        $item1->setName('Payment for order '.($this->order ? $this->order->order_no : '').' on '.$dbcfg->shop_firm_name)
            ->setCurrency('EUR')
            ->setQuantity(1)
            ->setPrice($totalPrice);

        $itemList = new \PayPal\Api\ItemList();
        $itemList->setItems([$item1]);

        $transaction = new \PayPal\Api\Transaction();
        $transaction->setAmount($amount)
            ->setDescription('Payment for order')
            ->setInvoiceNumber('123456')
            ->setItemList($itemList);

        $redirectUrls = new \PayPal\Api\RedirectUrls();
        $redirectUrls->setReturnUrl(
            path('kupshop_ordering_payment_legacypayment',
                array_merge([
                    'step' => 2,
                    'class' => $this->class,
                ], []),
                Router::ABSOLUTE_URL)
        )->setCancelUrl(
            path('kupshop_ordering_payment_legacypayment',
                array_merge([
                    'step' => 4,
                    'class' => $this->class,
                ], []),
                Router::ABSOLUTE_URL)
        );

        $payment = new \PayPal\Api\Payment();
        $payment->setIntent('sale')
            ->setPayer($payer)
            ->setTransactions([$transaction])
            ->setRedirectUrls($redirectUrls);

        try {
            $payment->create($apiContext);
        } catch (PayPal\Exception\PayPalConnectionException $e) {
            $data = $e->getData();
            if ($data) {
                $data = json_decode($data, true);
            }

            if (($data['name'] ?? false) === 'VALIDATION_ERROR') {
                $this->error(translate('PayPalConnectionException', 'paypal'));
                ServiceContainer::getService('logger')->error('PayPal exception: '.print_r($e->getData(), true),
                    ['file' => __FILE__, 'line' => __LINE__]);
            } else {
                throw $e;
            }
        }

        return $payment;
    }

    /** Funkce pro patch paymentu reálnými daty */
    public function updatePayment(): PayPal\Api\Payment
    {
        $payment = $this->getPayment();

        if ($useOriginalCurrency = in_array($this->order->currency, $this->allowedCurrencies)) {
            $totalPrice = roundPrice($this->order->getRemainingPayment())->asFloat();
        } else {
            $totalPrice = $this->order->convertPriceToEUR(
                roundPrice($this->order->getRemainingPayment())->asFloat()
            );
        }

        $patchAmount = new \PayPal\Api\Patch();
        $patchAddress = new \PayPal\Api\Patch();
        $patchPayer = new \PayPal\Api\Patch();
        $patchItems = new \PayPal\Api\Patch();
        $patchCustom = new \PayPal\Api\Patch();
        $patchInvoiceNo = new \PayPal\Api\Patch();

        $amount = new \PayPal\Api\Amount();
        $amount->setCurrency($useOriginalCurrency ? ($this->order ? $this->order->currency : '') : 'EUR')
            ->setTotal($totalPrice);

        $patchAmount->setOp('replace')
            ->setPath('/transactions/0/amount')
            ->setValue($amount);

        $payerDeliveryAddress = new \PayPal\Api\ShippingAddress();
        $payerDeliveryAddress->setState($this->order->delivery_state ?? '')
            ->setCountryCode($this->order->delivery_country ?? '')
            ->setCity($this->order->delivery_city ?? '')
            ->setLine1($this->order->delivery_street ?? '')
            ->setLine2($this->order->delivery_custom_address ?? '')
            ->setPostalCode($this->order->delivery_zip ?? '')
            ->setRecipientName($this->order->delivery_name.' '.$this->order->delivery_surname);

        $patchAddress->setOp('add')
            ->setPath('/transactions/0/item_list/shipping_address')
            ->setValue($payerDeliveryAddress);

        $payerInfoAddress = new \PayPal\Api\Address();
        $payerInfoAddress->setState($this->order->invoice_state ?? '')
            ->setCountryCode($this->order->invoice_country ?? '')
            ->setCity($this->order->invoice_city ?? '')
            ->setLine1($this->order->invoice_street ?? '')
            ->setLine2($this->order->invoice_custom_address ?? '')
            ->setPostalCode($this->order->invoice_zip ?? '');

        $payerInfo = new \PayPal\Api\PayerInfo();
        $payerInfo->setEmail($this->order ? $this->order->getUserEmail() : '')
            ->setBillingAddress($payerInfoAddress)
            ->setFirstName($this->order->invoice_name ?? '')
            ->setLastName($this->order->invoice_surname ?? '');

        $patchPayer->setOp('add')
            ->setPath('/payer/payer_info')
            ->setValue($payerInfo);

        $items = [];
        $itemList = new \PayPal\Api\ItemList();

        foreach ($this->order->fetchItems() as $orderItem) {
            $item = new \PayPal\Api\Item();
            $item->setName($orderItem['descr'])
                ->setCurrency($useOriginalCurrency ? $this->order->currency ?? '' : 'EUR')
                ->setQuantity($orderItem['pieces'])
                ->setPrice($orderItem['piece_price']['value_with_vat']->asFloat());
            $items[] = $item;
        }
        $itemList->setItems($items);

        $patchItems->setOp('replace')
            ->setPath('/transactions/0/item_list')
            ->setValue($itemList);

        $patchCustom->setOp('add')
            ->setPath('/transactions/0/custom')
            ->setValue($this->order->getSecurityCode());

        $patchInvoiceNo->setOp('add')
            ->setPath('/transactions/0/invoice_number')
            ->setValue($this->order->order_no);

        $patchRequest = new \PayPal\Api\PatchRequest();
        $patchRequest->setPatches([
            $patchAddress,
            $patchAmount,
            $patchPayer,
            $patchItems,
            $patchCustom,
            $patchInvoiceNo,
        ]);

        try {
            $payment->update($patchRequest, $this->getApiContext());
        } catch (Exception $ex) {
            ServiceContainer::getService('logger')->error('PayPal exception: '.$ex->getMessage(), ['file' => __FILE__, 'line' => __LINE__]);

            if ($ex instanceof \PayPal\Exception\PayPalConnectionException) {
                ServiceContainer::getService('logger')->error('PayPal exception: '.print_r($ex->getData(), true),
                    ['file' => __FILE__, 'line' => __LINE__]);
            }

            $this->error('storno');
        }

        return $payment;
    }

    public function processStep_1()
    {
        //   $this->error('');
    }

    public function processStep_2()
    {
        ServiceContainer::getService('logger')->error('PayPal response: '.print_r([$_GET, $_POST, $_SERVER], true),
            ['file' => __FILE__, 'line' => __LINE__]);
        $payPalPaymentID = getVal('paymentId', null, false);
        $apiContext = $this->getApiContext();
        $payment = $this->getPayPalPayment($apiContext, $payPalPaymentID);
        $error = false;
        if ($payment) {
            $this->orderId = $this->selectSQL('orders',
                ['order_no' => $payment->getTransactions()[0]->getInvoiceNumber()],
                ['id'])->fetchOne();
            $this->order = new Order();
            $this->order->createFromDB($this->orderId);
            if ($this->order->getSecurityCode() == $payment->getTransactions()[0]->getCustom()) {
                // Při platbě paypal umožňuje změnit adresu doručení....

                //                 $address = $payment->getTransactions()[0]->getItemList()->getShippingAddress()->toArray();
                //                $address = $payment->toArray();
                //                if (isset($address['payer']['payer_info']['first_name']) && isset($address['payer']['payer_info']['last_name'])) {
                // //                    $this->order->updateSQL('orders', [
                // //                        'delivery_name' => $address['payer']['payer_info']['first_name'],
                // //                        'delivery_surname' => $address['payer']['payer_info']['last_name'],
                // //                        'delivery_street' => $address['payer']['payer_info']['shipping_address']['line1'],
                // //                        'delivery_city' => $address['payer']['payer_info']['shipping_address']['city'],
                // //                        'delivery_zip' => $address['payer']['payer_info']['shipping_address']['postal_code'],
                // //                        'delivery_country' => $address['payer']['payer_info']['shipping_address']['country_code'],
                // //                        'delivery_phone' => $address['payer']['payer_info']['phone'] ?? '',
                // //                        'delivery_state' => $address['payer']['payer_info']['shipping_address']['state'],
                // //                    ], ['id' => $this->orderId]);
                //
                //                    $name = explode(' ', $address['recipient_name']);
                //                    $firstname = $name[0] ?? '';
                //                    unset($name[0]);
                //                    $lastname = implode(' ', $name);
                //                    $this->order->updateSQL('orders', [
                //                        'delivery_name' => $firstname,
                //                        'delivery_surname' => $lastname ?? '',
                //                        'delivery_street' => $address['line1'],
                //                        'delivery_city' => $address['city'],
                //                        'delivery_zip' => $address['postal_code'],
                //                        'delivery_country' => $address['country_code'],
                //                        'delivery_phone' => $address['payer']['payer_info']['phone'] ?? '',
                //                        'delivery_state' => $address['payer']['payer_info']['shipping_address']['state'],
                //                    ], ['id' => $this->orderId]);
                //                }

                $this->createPayment(
                    $payment->getId(),
                    $payment->getTransactions()[0]->getAmount()->getTotal(),
                    ['paymentClass' => self::class]
                );
                $this->processPayment($payment);

                $paymentInstructions = $payment->getPaymentInstruction();
                if ($paymentInstructions) {
                    $paypalData = $this->order->getData('paypalplus');
                    $paypalData['payment_instruction'] = $paymentInstructions->toArray();
                    $this->order->setData('paypalplus', $paypalData);
                }

                $this->step(3, 'wait', ['paymentId' => $payment->getId()]);
            } else {
                $error = true;
            }
        } else {
            $error = true;
        }

        if ($error) {
            $this->error(translate('payment_status_check_error', 'payment'));
        }
    }

    public function processStep_3()
    {
        $apiContext = $this->getApiContext();
        $payPalPaymentID = getVal('paymentId', null, false);
        if (!$payPalPaymentID) {
            $kupshopPayment = $this->getPendingPayment();
            if ($kupshopPayment) {
                // use already created payment
                $payPalPaymentID = $kupshopPayment['decoded_data']->session;
            } else {
                $this->error('storno');
            }
        }
        $this->processPayment($this->getPayPalPayment($apiContext, $payPalPaymentID));
    }

    public function processStep_4()
    {
        $token = getVal('token', null, false);
        if (!$token) {
            $this->error('storno');
        }
        $payPalId = sqlQueryBuilder()
            ->select("id, json_extract(note_admin, '$.paypalplus.id') as paypalID")
            ->from('orders')
            ->where("json_extract(note_admin, '$.paypalplus.token') = :token")
            ->setParameter('token', $token)
            ->execute()
            ->fetchAssociative();

        if (!$payPalId) {
            $this->error('storno');
        }
        $this->orderId = $payPalId['id'];
        $this->order = new \Order();
        $this->order->createFromDB($this->orderId);
        $this->error(translate('payment_storno', 'payment'));
    }

    public static function getSettingsConfiguration(): array
    {
        return [
            'fields' => [
                'clientID' => [
                    'title' => 'clientID',
                    'type' => 'text',
                ],
                'secret' => [
                    'title' => 'secret',
                    'type' => 'text',
                ],
                'mode' => [
                    'title' => 'Režim',
                    'type' => 'select',
                    'options' => ['live' => 'Produkční režim', 'sandbox' => 'Testovaci režim'],
                ],
            ],
        ];
    }
}
