<?php

if (!defined('BASEPATH')) {
    exit('No direct script access allowed');
}

use PayPal\Auth\OAuthTokenCredential;
use PayPal\Rest\ApiContext;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\RedirectUrls;
use PayPal\Api\PaymentExecution;

/**
 * Description of paypal
 *
 * @author Igorl Ratsun
 */
class Paypal extends MX_Controller
{

    const STATUS_APPROVED = 'approved';

    public function __construct()
    {
        parent::__construct();
        $this->config->load('paypal/credential');
        $this->load->helper('logger/logger');
        $this->load->model('paypal/paypal_tmodel');
        $this->load->library('paypal/paypal_pages');
    }

    /**
     * Create paypal payment
     * @param array $data
     * $data = [
      'currency'    => 'USD',
      'total'       => '50',
      'tax'         => '0.15',
      'invoice'     => uniqid(),
      'description' => 'description',
      ];
     * @param bool $auto_redirect redirect to PayPal or return PayPal link
     */
    public function pay($data = [], $auto_redirect = true)
    {
        if (!isset($data['currency']) || !isset($data['total']) || !isset($data['invoice']) || !isset($data['description'])) {
            show_error('Some data is missing');
        }
        $payer = new Payer();
        $payer->setPaymentMethod("paypal");

        $redirectUrls = new RedirectUrls();
        $redirectUrls->setReturnUrl(base_url('/ajax/paypal/paypal/execute?success=true'))
                ->setCancelUrl(base_url('/ajax/paypal/paypal/execute?success=false'));

        $payment = new Payment();
        $payment->setIntent("sale")
                ->setPayer($payer)
                ->setRedirectUrls($redirectUrls)
                ->setTransactions([$this->get_transaction($data)]);

        try {
            $payment->create($this->get_api_context());
        } catch (Exception $ex) {
            logger_add(__FUNCTION__, $payment);
            logger_add(__FUNCTION__, $ex->getMessage());
            show_error('Payment error. Please, try again later.');
        }

        // Insert data
        $this->paypal_tmodel->add($data);

        logger_add(__FUNCTION__, $data);

        $link = $payment->getApprovalLink();

        if ($auto_redirect) {

            redirect($link);
        } else {

            return $link;
        }
    }

    /**
     * Check payment
     */
    public function execute()
    {
        if ($this->input->get('success') == 'true') {
            logger_add(__FUNCTION__, ['GET' => $this->input->get()]);

            $paymentId = $this->input->get('paymentId');
            $apiContext = $this->get_api_context();
            $payment = Payment::get($paymentId, $apiContext);

            // Fetch data from order
            $data = $this->paypal_tmodel->fetch_by_invoice(isset($payment->transactions[0]->invoice_number) ? (string) $payment->transactions[0]->invoice_number : 0);
            if (empty($data)) {
                logger_add(__FUNCTION__, 'Empty $data');

                redirect($this->paypal_pages->cancel($data));
            }

            if (isset($payment->state) && $payment->state == self::STATUS_APPROVED) {
                logger_add(__FUNCTION__, 'Payment status already approved');

                redirect($this->paypal_pages->success($data));
            }

            $execution = new PaymentExecution();
            $execution->setPayerId($this->input->get('PayerID'));
            $execution->addTransaction($this->get_transaction($data));
            try {
                $payment->execute($execution, $apiContext);
            } catch (Exception $ex) {
                logger_add(__FUNCTION__, $ex->getMessage());
                show_error('Payment error. Please, try again later.');
            }

            $finish = Payment::get($paymentId, $apiContext);

            if (isset($finish->state) && $finish->state == self::STATUS_APPROVED) {
                $this->paypal_tmodel->update($data['id_paypal'], ['paid' => '1']);
                logger_add(__FUNCTION__, sprintf('Payment `%s` approved', $data['id_paypal']));

                redirect($this->paypal_pages->success($data));
            }
        }

        redirect($this->paypal_pages->cancel(null));
    }

    protected function get_api_context()
    {
        $apiContext = new ApiContext(new OAuthTokenCredential($this->config->item('paypal_client_id'), $this->config->item('paypal_client_secret')));

        $apiContext->setConfig([
            'mode' => $this->config->item('paypal_mode'),
        ]);

        return $apiContext;
    }

    protected function get_transaction($data = [])
    {
        return $this->load->library('paypal/paypal_transaction')->prepare($data);
    }

}
