<?php
namespace App\Traits\PaymentGateway;

use Exception;
use Illuminate\Support\Str;
use App\Models\TemporaryData;
use App\Http\Helpers\PaymentGateway;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use App\Constants\PaymentGatewayConst;
use App\Http\Controllers\Api\V1\User\AddMoneyController;
use App\Http\Controllers\Frontend\AppointmentBookingController;
use App\Http\Controllers\User\BuyCryptoController;
use App\Http\Controllers\User\RemittanceController;
use App\Models\DoctorAppointment;
use Illuminate\Http\Client\RequestException;
use Illuminate\Http\Request;

trait Flutterwave { 

    private $flutterwave_gateway_credentials;
    private $request_credentials;
    private $flutterwave_api_base_url = "https://api.flutterwave.com/";
    private $flutterwave_api_v3       = "v3";

    public function flutterwaveInit($output = null) {
        if(!$output) $output = $this->output;
        $request_credentials = $this->getFlutterwaveRequestCredentials($output);
        
        return $this->createFlutterwavePaymentLink($output, $request_credentials);
    }


    public function registerFlutterwaveEndpoints($endpoint_key = null) 
    {
        $endpoints = [
            'create-payment-link'       => $this->flutterwave_api_base_url . $this->flutterwave_api_v3 . "/payments",
        ];

        if($endpoint_key) {
            if(!array_key_exists($endpoint_key, $endpoints)) throw new Exception("Endpoint key [$endpoint_key] not registered! Register it in registerFlutterwaveEndpoints() method");

            return $endpoints[$endpoint_key];
        }

        return $endpoints;
    }

    public function createFlutterwavePaymentLink($output, $request_credentials) {

        $endpoint = $this->registerFlutterwaveEndpoints('create-payment-link');
        
        $temp_record_token = generate_unique_string('temporary_datas','identifier',60);
        $this->setUrlParams("token=" . $temp_record_token); // set Parameter to URL for identifying when return success/cancel

        $redirection = $this->getRedirection();
        $url_parameter = $this->getUrlParams();
        if(auth()->check()){
            $user = auth()->guard(get_auth_guard())->user();
        }else{
            $user  = DoctorAppointment::where('slug',$output['form_data']['identifier'])->first();
        }
        $booking_data   = DoctorAppointment::where('slug',$output['form_data']['identifier'])->first();
        if($booking_data->authenticated == true){
            $temp_data = $this->flutterWaveJunkInsert($temp_record_token); // create temporary information
        }else{
            $temp_data = $this->flutterWaveJunkInsertForUnAuth($temp_record_token);
        }
        $response = Http::withHeaders([
            'Authorization' => 'Bearer ' . $request_credentials->token,
        ])->post($endpoint,[
            'tx_ref'        => $temp_record_token,
            'amount'        => $output['amount']->total_amount,

            'currency'      => $output['currency']->currency_code,
            'redirect_url'  => $this->setGatewayRoute($redirection['return_url'],PaymentGatewayConst::FLUTTERWAVE,$url_parameter),
            'customer'      => [
                'email' => $user->email,
                'name'  => $user->firstname ?? "",
            ],
            'customizations'    => [
                'title'     => "Appoitment Booking",
                'logo'      => get_fav(),
            ]
        ])->throw(function(Response $response, RequestException $exception) use ($temp_data) {
            $temp_data->delete();
            throw new Exception($exception->getMessage());
        })->json();

        $response_array = json_decode(json_encode($response), true);

        $temp_data_contains = json_decode(json_encode($temp_data->data),true);
        $temp_data_contains['response'] = $response_array;

        $temp_data->update([
            'data'  => $temp_data_contains,
        ]);


        // make api response
        if(request()->expectsJson()) {
            $this->output['redirection_response']   = $response_array;
            $this->output['redirect_links']         = [];
            $this->output['redirect_url']           = $response_array['data']['link'];
            return $this->get();
        }

        return redirect()->away($response_array['data']['link']);
    }

    public function flutterWaveJunkInsert($temp_token) {
        $output = $this->output;

        $data = [
            'gateway'       => $output['gateway']->id,
            'currency'      => [
                'id'        => $output['currency']->id,
                'alias'     => $output['currency']->alias
            ],
            'payment_method'=> $output['currency'],
            'amount'        => json_decode(json_encode($output['amount']),true),
            'creator_table' => auth()->guard(get_auth_guard())->user()->getTable() ?? '',
            'creator_id'    => auth()->guard(get_auth_guard())->user()->id ?? '',
            'creator_guard' => get_auth_guard() ?? '',
            'user_record'   => $output['form_data']['identifier'],
        ];

        return TemporaryData::create([
            'type'          => PaymentGatewayConst::FLUTTERWAVE,
            'identifier'    => $temp_token,
            'data'          => $data,
        ]);
    }
    public function flutterWaveJunkInsertForUnAuth($temp_token) {
        $output = $this->output;

        $data = [
            'gateway'       => $output['gateway']->id,
            'currency'      => [
                'id'        => $output['currency']->id,
                'alias'     => $output['currency']->alias
            ],
            'payment_method'=> $output['currency'],
            'amount'        => json_decode(json_encode($output['amount']),true),
            
            
            'user_record'   => $output['form_data']['identifier'],
        ];

        return TemporaryData::create([
            'type'          => PaymentGatewayConst::FLUTTERWAVE,
            'identifier'    => $temp_token,
            'data'          => $data,
        ]);
    }

    public function getFlutterwaveCredentials($output)
    {
        $gateway = $output['gateway'] ?? null;
        if(!$gateway) throw new Exception("Payment gateway not available");

        $public_key_sample = ['public key','test key','sandbox public key','public', 'test public','flutterwave public key', 'flutterwave public'];
        $secret_key_sample = ['secret','secret key','flutterwave secret','flutterwave secret key'];
        $encryption_key_sample    = ['encryption','encryption key','flutterwave encryption','flutterwave encryption key'];

        $public_key    = PaymentGateway::getValueFromGatewayCredentials($gateway,$public_key_sample);
        $secret_key         = PaymentGateway::getValueFromGatewayCredentials($gateway,$secret_key_sample);
        $encryption_key    = PaymentGateway::getValueFromGatewayCredentials($gateway,$encryption_key_sample);
        
        $mode = $gateway->env;
        $gateway_register_mode = [
            PaymentGatewayConst::ENV_SANDBOX => PaymentGatewayConst::ENV_SANDBOX,
            PaymentGatewayConst::ENV_PRODUCTION => PaymentGatewayConst::ENV_PRODUCTION,
        ];

        if(array_key_exists($mode,$gateway_register_mode)) {
            $mode = $gateway_register_mode[$mode];
        }else {
            $mode = PaymentGatewayConst::ENV_SANDBOX;
        }

        $credentials = (object) [
            'public_key'                => $public_key,
            'secret_key'                => $secret_key,
            'encryption_key'            => $encryption_key,
            'mode'                      => $mode
        ];

        $this->flutterwave_gateway_credentials = $credentials;

        return $credentials;
    }

    public function getFlutterwaveRequestCredentials($output = null) 
    {
        if(!$this->flutterwave_gateway_credentials) $this->getFlutterwaveCredentials($output);
        $credentials = $this->flutterwave_gateway_credentials;
        if(!$output) $output = $this->output;

        $request_credentials = [];
        if($output['gateway']->env == PaymentGatewayConst::ENV_PRODUCTION) {
            $request_credentials['token']   = $credentials->secret_key;
        }else {
            $request_credentials['token']   = $credentials->secret_key;
        }

        $this->request_credentials = (object) $request_credentials;
        return (object) $request_credentials;
    }

    public function isFlutterwave($gateway) 
    {
        $search_keyword = ['flutterwave','flutterwave gateway','gateway flutterwave','flutterwave payment gateway'];
        $gateway_name = $gateway->name;

        $search_text = Str::lower($gateway_name);
        $search_text = preg_replace("/[^A-Za-z0-9]/","",$search_text);
        foreach($search_keyword as $keyword) {
            $keyword = Str::lower($keyword);
            $keyword = preg_replace("/[^A-Za-z0-9]/","",$keyword);
            if($keyword == $search_text) {
                return true;
                break;
            }
        }
        return false;
    }

    public function flutterwaveSuccess($output) {

        $redirect_response = $output['tempData']['data']->callback_data ?? false;
        if($redirect_response == false) {
            throw new Exception("Invalid response");
        }
        
        if($redirect_response->status == "cancelled") {

            $identifier = $output['tempData']['identifier'];
            $response_array = json_decode(json_encode($redirect_response), true);

            if(isset($response_array['r-source']) && $response_array['r-source'] == PaymentGatewayConst::APP) {
                if($output['type'] == PaymentGatewayConst::FLUTTERWAVE) {
                    return (new AppointmentBookingController())->cancel(new Request([
                        'token' => $identifier,
                    ]), PaymentGatewayConst::FLUTTERWAVE);
                }
            }

            $this->setUrlParams("token=" . $identifier); // set Parameter to URL for identifying when return success/cancel
            $redirection = $this->getRedirection();
            
            $url_parameter = $this->getUrlParams();

            $cancel_link = $this->setGatewayRoute($redirection['cancel_url'],PaymentGatewayConst::FLUTTERWAVE,$url_parameter);
            return redirect()->away($cancel_link);
        }

        if($redirect_response->status == "successful") {
            $output['capture']      = $output['tempData']['data']->response ?? "";
            
            try{
                $status = global_const()::APPROVED;
                $transaction_response = $this->createTransaction($output,$status);
            }catch(Exception $e) {
                throw new Exception($e->getMessage());
            }
        }
        return $transaction_response;

    }

}