<?php

namespace apexl\Io\modules\payment\controllers;

use apexl\Io\includes\Controller;
use apexl\Io\includes\Hook;
use apexl\Io\includes\Routes;
use apexl\Io\includes\System;
use apexl\Io\includes\Utils;
use apexl\Io\modules\payment\entities\paymentTokenEntity;
use apexl\Io\modules\payment\services\cardValidationService;
use apexl\Io\modules\payment\services\paymentService;
use apexl\Io\modules\user\entities\userEntity;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class billingController extends Controller
{
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Redirect user to payment provider to validate payment details
     * @param Request $request
     * @param Response $response
     * @return Response|void
     * @throws \Exception
     */
    public function validation(Request $request, Response $response){
        $body = (object)$request->getQueryParams();

        if (isset($body->args)) $body = json_decode(base64_decode($body->args));

        $data = (object)[];
        $allowedProps = ['userEmail', 'userFirstName', 'userLastName', 'productId', 'products', 'total', 'order_id', 'order_complete_url', 'order_failed_url', 'startDate', 'nextBillingDate', 'gracePeriodEnd', 'create_subscription','meta','addPaymentMethod'];
        $allowedProps = Hook::processHook('validation_payments_set_allowed_properties', $allowedProps);

        foreach ($allowedProps as $prop) {
            if (Utils::checkExistsAndNotEmpty($prop, $body) || (in_array($prop, ['total', 'create_subscription']) && isset($body->$prop))) {
                $data->$prop = $body->$prop;
            }
        }

        $validation_fields = [];
        //$validation_fields = ['order_id', 'total'];
        $validation_fields = Hook::processHook('validation_validate_payment_data_fields', $validation_fields);

        $valid = TRUE;
        foreach($validation_fields as $field){
            if(!isset($data->$field)){
                $valid = FALSE;
            }
        }
        //Not valid? Allow modules to override the validation state if required - for example, adding more complex validation rules.
        $valid = Hook::processHook('validation_alter_validate_payment_data', $valid, $validation_fields, $data);

        if (!$valid) {
            //Still not valid? Error out.
            return System::asJson($response, ['error' => 'The following request parameters are required or invalid: '.implode(", ", $validation_fields)], 400);
        }

        $cardValidationService = new cardValidationService();
        $paymentService = new paymentService();

        if (isset($data->subscription) && $data->subscription == '0') {
            $paymentService->createPendingPayment($body->total, $body, $body->order_id);

            $urls = Hook::processHook('io_payment_oneoff_return_urls', $data);
            $successUrl = $urls->successUrl ?? $this->config->app->site->backend_domain . Routes::getRoutePattern('payment.oneoff.success').'/'.$body->order_id;
            $cancelUrl = $urls->cancelUrl ?? $this->config->app->site->backend_domain . Routes::getRoutePattern('payment.oneoff.cancel').'/'.$body->order_id;

            $providerUrl = $cardValidationService->getOneTimePaymentUrl($body->products, $successUrl, $cancelUrl);
        } else {

            $user = new userEntity();
            //allow other modules to act on how the user is loaded.
            $user = Hook::processHook('validation_alter_user_load', $user, $data);

            //no loaded user? Try load one.
            if (!$user->id && isset($data->userEmail)) {
                $user->getUserByEmail($data->userEmail);
            }

            $paymentTokenEntity = $cardValidationService->createNewTokenEntity($user->id ?? null, $data);

            $urls = Hook::processHook('io_payment_validation_return_urls', $data);

            $successUrl = $urls->successUrl ?? $this->config->app->site->backend_domain . Routes::getRoutePattern('payment.validation.success');
            $cancelUrl = $urls->cancelUrl ?? $this->config->app->site->backend_domain . Routes::getRoutePattern('payment.validation.cancel', ['localTokenId' => $paymentTokenEntity->id]);

            $providerUrl = $cardValidationService->getProviderPortalUrl($paymentTokenEntity, $successUrl, $cancelUrl, json_decode($paymentTokenEntity->data, true)['meta'] ?? null );
        }

        header('location: ' . $providerUrl); // Redirect to payment gateway
        exit();
    }

    /**
     * Process success response from payment provider
     * @param Request $request
     * @param Response $response
     * @param $args
     * @return void
     * @throws \Exception
     */
    public function validationSuccess(Request $request, Response $response, $args)
    {
        // Array ( [permission] => AllowAll [providerSessionId] => cs_test_c110G53f6aPz7Qdy8j7BI01mfoINHepdyI2wsSDj4C7Ga575vBYphw74Fb [localTokenId] => 3 )
        $paymentTokenEntity = new paymentTokenEntity();
        $paymentTokenEntity->load($args['localTokenId']);
        $paymentTokenEntity->encryptProviderSession($args['providerSessionId']);

        if ($paymentTokenEntity->validated) {

            $user = new userEntity();
            $user->load($paymentTokenEntity->user_id);

            if ($user->id) {
                //Got a user? Check if we need to force a login. If not, validate the token and return.
                $renewRequiresLogin = System::getVariable('payments_require_login_to_validate_replacement_token') ?? FALSE;
                if(!$renewRequiresLogin){
                    //Validate the token in place.
                    $paymentTokenEntity->validateToken($request, ["user" => $user, "type" => "renewToken", "providerSessionId" => $args['providerSessionId']]);
                    //send user to thank you page
                    Hook::processHook('validation_payments_post_token_creation', $paymentTokenEntity);

                    $redirectUrl = Hook::processHook('io_payment_validation_success_redirect', $paymentTokenEntity);
                    header('bb-redirect-url:'.$redirectUrl);
                    if ($redirectUrl && $redirectUrl != 'no-redirect') {
                        System::redirect($redirectUrl);
                    }

                    System::redirect($this->config->app->site->frontend_domain. Routes::getRoutePattern('payment.display.setup-complete'));

                } else {
                    header('location: ' . $this->config->app->site->frontend_domain . Routes::getRoutePattern('billing.display.loginaccount', ['localTokenId' => $paymentTokenEntity->id]));
                    exit();
                }
            }

            $newTokenRequiresLogin = System::getVariable('payments_require_login_to_create_token') ?? FALSE;
            if(!$newTokenRequiresLogin){
                //We should have the user email here, so we can use that to setup the record.
                $data = json_decode($paymentTokenEntity->data);

                //No login needed? We'll still have to setup an account, so lets do that.
                $user = new userEntity();
                $user->email = $data->userEmail;
                $user->first_name = $data->userFirstName ?? strtok($data->userEmail, '@');
                $user->last_name = $data->userLastName ?? "Auto";

                //this process will create a user if needed, and charge if this is a brand new token.
                $paymentTokenEntity->validateToken($request, ["user" => $user, "type" => "newToken", "userPassword" => bin2hex(random_bytes(12)), "providerSessionId" => $args['providerSessionId']]);

                $redirectUrl = Hook::processHook('io_payment_validation_success_redirect', $paymentTokenEntity);
                header('bb-redirect-url:'.$redirectUrl);
                if ($redirectUrl) {
                    System::redirect($redirectUrl);
                }

                //send user to Order Page.
                if($data->order_complete_url ?? FALSE){
                    System::redirect($data->order_complete_url);
                }

                System::redirect($this->config->app->site->frontend_domain. Routes::getRoutePattern('payment.display.setup-complete'));
            } else {
                // Redirect user to sign-up page
                header('location: ' . $this->config->app->site->frontend_domain . Routes::getRoutePattern('billing.display.activateaccount', ['localTokenId' => $paymentTokenEntity->id]));
                exit();
            }
        }
    }

    /**
     * Deal with failed validation attempts
     * @param Request $request
     * @param Response $response
     * @param $args
     * @return void
     */
    public function validationCancel(Request $request, Response $response, $args)
    {
        header('location: ' . $this->config->app->site->frontend_domain.Routes::getRoutePattern('billing.display.validationfailed', ['localTokenId' => $args['localTokenId']]));
        exit();
    }
}
