<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\PaymentExecution;
use PayPal\Api\RedirectUrls;
use PayPal\Api\Transaction;
use PayPal\Rest\ApiContext;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Exception\PayPalConnectionException;
use App\Services\Contracts\ParkServiceInterface;
use App\Library\FinancialLib;
use App\Exceptions\EmptyCartException;
use Mail;
use DB;
use App\Mail\ParkCreated;
use App\Models\Transaction as WalletTransaction;

class PaymentController extends Controller
{
    private $apiContext;
    private $shipping   = 0;
    private $tax        = 0;
    private $subtotal   = 0;
    
    public function __construct() {
        $paypalConf = config('paypal');

        $this->apiContext = new ApiContext(new OAuthTokenCredential(
            $paypalConf['client_id'],
            $paypalConf['secret']
        ));
        $this->apiContext->setConfig($paypalConf['settings']);
    }

    public function create(ParkServiceInterface $service) {
        try {
            $payment = $this->buildPayment($service);
        
            $payment->create($this->apiContext);
            session()->put('paypal_payment_id', $payment->getId());
            
            $approvalUrl = $payment->getApprovalLink();
        } catch (PayPalConnectionException $ex) {
            $ex->getTrace();
            
            $this->flashSessionMessage('danger', trans('messages.cart.paypal.danger.generic'));

            return redirect('/payment/result');
        } catch (EmptyCartException $ex) {
            $this->flashSessionMessage('warning', $ex->getMessage());
            
            return redirect('/payment/result');
        }

        if (isset($approvalUrl)) {
            return redirect()->away($approvalUrl);
        }

        $this->flashSessionMessage('danger', trans('messages.cart.paypal.danger.unknown'));

        return redirect('/payment/result');
    }
    
    private function buildPayment($service) {
        $payer = new Payer();
        $payer->setPaymentMethod("paypal");
        
        $redirectUrls = new RedirectUrls();
        $redirectUrls->setReturnUrl(url('/payment/execute'))
            ->setCancelUrl(url('/payment/execute'));
        
        $transaction = $this->buildTransaction($service);
        
        $payment = new Payment();
        $payment->setIntent("sale")
            ->setPayer($payer)
            ->setRedirectUrls($redirectUrls)
            ->setTransactions(array($transaction));
        
        return $payment;
    }
    
    private function buildTransaction($service) {
        $itemsList = $this->getItemsList($service);
        $amount = $this->getAmount();
        
        $transaction = new Transaction();
        $transaction->setAmount($amount)
            ->setItemList($itemsList)
            ->setDescription("Pagamento Sosta D-PASS")
            ->setInvoiceNumber(uniqid('pp-'));
        
        return $transaction;
    }
    
    private function getItemsList(ParkServiceInterface $service) {
        $items = [];
            
        $item = new Item();
            
        $parkItem = $service->getParkItem();
        if (is_null($parkItem)) {
            throw new EmptyCartException(trans('messages.cart.paypal.warning.empty_cart'));
        }
        
        $price = $parkItem->costo;
        $subtotal = FinancialLib::centToEuro($price, false, '.');

        $item->setName("Sosta in " . $parkItem->level->area->indirizzo)
            ->setCurrency('EUR')
            ->setQuantity(1)
            ->setSku('park-' . $parkItem->id . '-l-' . $parkItem->livello_id)
            ->setPrice($subtotal);

        $this->subtotal += $subtotal;

        $items[] = $item;

        $itemsList = new ItemList();
        $itemsList->setItems($items);
        
        return $itemsList;
    }
    
    private function getAmount() {
        $details = new Details();
        $details->setShipping($this->shipping)
            ->setTax($this->tax)
            ->setSubtotal($this->subtotal);
        
        $amount = new Amount();
        $amount->setCurrency('EUR')
            ->setTotal($this->shipping + $this->tax + $this->subtotal)
            ->setDetails($details);
        
        return $amount;
    }


    public function createlocal(Request $request, ParkServiceInterface $service) {

        try {
                DB::beginTransaction();

                $service->setActive();

                $park = $service->getParkItem();
                $park->load('user');
                
                $transaction = WalletTransaction::create([
                    'utente_id' => $park->utente_id,
                    'importo' => $park->costo,
                    'causale' => "Pagamento sosta D-PASS (fast)",
                    'tabella' => 'spl_soste',
                    'tabella_riga' => $park->id,
                    'esito' => WalletTransaction::COMPLETATA,
                    'fast' => true,
                ]);

                DB::commit();

                $this->flashSessionMessage('success', trans('messages.cart.paypal.success.success'));

                try {
                    //Mail::to($park->user->email)->send(new ParkCreated($park));
                } catch (Exception $e) {
                    //dd($e);
                }
                
                return redirect('/payment/result');

        } catch (Exception $ex) {
            DB::rollback();
            dd($ex);
            //$this->flashSessionMessage('danger', trans('messages.cart.paypal.danger.generic'));
        }

        session()->put('danger', "Payment failed");
        
        return redirect('/payment/result');
    }

    public function execute(Request $request, ParkServiceInterface $service) {
		if (!$request->has('paymentId')) {
            $this->flashSessionMessage('warning', trans('messages.cart.paypal.warning.canceled'), true);
            
            return redirect('/payment/result');
        }
        
        if (!($request->has('token') && $request->has('PayerID'))) {
            $this->flashSessionMessage('danger', trans('messages.cart.paypal.danger.failed'));

            return redirect('/payment/result');
        }

        try {
            $result = $this->makePayment($request->get('PayerID'));
            if ($result->getState() == 'approved') {
                DB::beginTransaction();

                $service->setActive();

                $park = $service->getParkItem();
                $park->load('user');
                
                $transaction = WalletTransaction::create([
                    'utente_id' => $park->utente_id,
                    'importo' => $park->costo,
                    'causale' => "Pagamento sosta D-PASS (fast)",
                    'tabella' => 'spl_soste',
                    'tabella_riga' => $park->id,
                    'esito' => WalletTransaction::COMPLETATA,
                    'fast' => true,
                ]);

                DB::commit();

                $this->flashSessionMessage('success', trans('messages.cart.paypal.success.success'));

                try {
                    Mail::to($park->user->email)->send(new ParkCreated($park));
                } catch (Exception $e) {
                    //
                }
                
                return redirect('/payment/result');
            }
        } catch (PayPalConnectionException $e) {
            DB::rollback();

            $this->flashSessionMessage('danger', trans('messages.cart.paypal.danger.generic'));
        }

        session()->put('danger', "Payment failed");
        
        return redirect('/payment/result');
    }

    private function makePaymentLocal($payerId) {
        $paymentId = 0;
        
        $payment = Payment::get($paymentId, $this->apiContext);
        $execution = new PaymentExecution();
        $execution->setPayerId(0);
        
        return $payment->execute($execution, $this->apiContext);
    }    

    private function makePayment($payerId) {
        $paymentId = session()->get('paypal_payment_id');
        
        $payment = Payment::get($paymentId, $this->apiContext);
        $execution = new PaymentExecution();
        $execution->setPayerId($payerId);
        
        return $payment->execute($execution, $this->apiContext);
    }
    
    private function flashSessionMessage($type, $text, $flush = false) {
        if ($flush) {
            session()->flush();
        }
        
        session()->flash('message', [
            'type'  => $type,
            'text'  => $text,
        ]);
    }
    
    public function result(ParkServiceInterface $service) {
        $message = null;
        if (session()->has('message')) {
            $message = (object) session()->get('message');
        }
        
        $parkItem = $service->getParkItem();
        
        session()->flush();
        
        return view('payments.result', compact('message', 'parkItem'));
    }
}
