<?php

namespace App\PayoutCF;

use App\CFPayoutDirectTransfer;
use App\CFPayoutPreserve;
use App\JobProvider;
use App\Slack;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class PayoutCFAPI extends CF_Logs{

    private $request;
    private $payload = array();
    public $can_disbursed = true;
    /**
     * @var false|mixed|string
     */
    private $endpoint;
    /**
     * @var bool|string
     */
    public $response = null;
    private $headers = array();
    private $token = false;
    public $error = false;
    public $isRefund = false;
    /**
     * @var string[]
     */
    private $apiAttr;
    /**
     * @var bool|mixed|string|null
     */

    public function __construct(Request $request){
        $this->request = $request;
        $this->endpoint = _env('CASHFREE_ENDPOINT');
        $this->initAuthorization();
    }

    public function prepare(){
        $payoutStatusData = CFPayoutDirectTransfer::whereIn('message',['SUCCESS','FAILURE'])->get(['application_id']);
        $idsToBeChecked = [];
        foreach ($payoutStatusData as $payoutStatusDataItem){
            $idsToBeChecked[] = $payoutStatusDataItem->application_id;
        }
        $payoutStatus = new CFPayoutDirectTransfer();
        if($idsToBeChecked) {
            $payoutStatus = $payoutStatus->whereNotIn('application_id', $idsToBeChecked);
        }
        $payoutStatus = $payoutStatus->get(['application_id', 'message']);

        if(!count($payoutStatus)){
            $this->can_disbursed = true;
        }
        return $this;
    }

    /**
     * @return false
     */
    public function initAuthorization(){
        $this->apiAttr = CFConfiguration::$authorize;
        $this->endpoint = $this->endpoint.$this->apiAttr['api'];
        $this->payload = array();
        if ($this->connect()){
            $this->token = $this->response->data->token ?? '';
        }
        $this->endpoint = _env('CASHFREE_ENDPOINT');
        return $this->token;
    }

    public function getBeneficiaryDetails(){
        switch ($this->isBenIDExists($this->request)){
            case self::CREATE:
            case self::REVISE:
                return $this->addBeneficiary();
                break;
            case self::EXISTS:
                return $this->request->get('beneId');
                break;
        }
        return false;
    }

    /**
     * @return bool|string|null
     */
    private function addBeneficiary(){

        if (!$this->token)
            return false;

        $this->apiAttr = CFConfiguration::$addBeneficiary;
        $this->endpoint = $this->endpoint.$this->apiAttr['api'];
        $this->payload = [
            "beneId" => $this->request->get('beneId'),
            "name" => $this->request->get('name'),
            "email" => $this->request->get('email'),
            "phone" => $this->request->get('phone'),
            "bankAccount" => $this->request->get('bankAccount'),
            "ifsc" => $this->request->get('ifsc'),
            "address1" => $this->request->get('address1'),
            "city" => $this->request->get('city'),
            "state" => $this->request->get('state'),
            "pincode" => $this->request->get('pincode')
        ];
        $this->headers = ['Authorization: Bearer '.$this->token];
        if ($this->connect()){
            $this->addBeneficiaryLog($this->request, $this->response);
            return $this->response->data->beneId ?? false;
        }
        return false;
    }

    /**
     * @return bool|string|null
     */
    private function getBeneficiary(){

        if (!$this->token)
            return false;

        $this->apiAttr = CFConfiguration::$getBeneficiary;
        $this->endpoint = $this->endpoint.$this->apiAttr['api'].'/'.$this->request->get('beneId');
        $this->headers = ['Authorization: Bearer '.$this->token];
        if ($this->connect()){
            $this->updateBeneficiaryLog($this->request, $this->response);
            return $this->response;
        }
        return false;
    }

    /**
     * @return bool|string|null
     */
    public function requestTransfer(){

        if (!$this->token)
            return false;

        $this->apiAttr = CFConfiguration::$requestTransfer;
        $this->endpoint = $this->endpoint.$this->apiAttr['api'];
        $this->payload = [
            "beneId" => $this->request->get('beneId'),
            "amount" => $this->request->get('amount'),
            "transferId" => $this->request->get('transferId'),
        ];
        $this->headers = ['Authorization: Bearer '.$this->token];
        if ($this->connect()){
            $this->requestTransferLog($this->request, $this->response);
            return $this->response;
        }
        return false;
    }

    /**
     * @return bool|bool[]|null[]|string|string[]|null
     */
    public function getTransferStatus(){

        if (!$this->token)
            return false;

        $this->apiAttr = CFConfiguration::$getTransferStatus;
        $this->payload = [
            'referenceId' => $this->request->get('referenceId'),
            'transferId' => $this->request->get('transferId')
        ];
        $this->endpoint = $this->endpoint.$this->apiAttr['api'];
        $this->headers = ['Authorization: Bearer '.$this->token];

        if ($this->connect()){
            if ($this->isRefund){
                return ['response' => $this->response->data->transfer ?? (object) ['status' => 'FAILURE']];
            }
            $this->getTransferStatusLog($this->request, $this->response);
            return ['response' => $this->response->data->transfer ?? (object) ['status' => 'FAILURE']];
        }
        if ($this->error){
            (new Slack(new Request(['title' =>'Get Transfer Status Error', 'fields' => ['endpoint' => $this->endpoint], 'group' => Slack::$STAGE_TESTING])))->send();
            //acknowledged -> 5 // Failure
            $this->directTransferLog($this->request, (object) ['data' => (object)['acknowledged' => 5, 'utr' => '', 'referenceId' => $this->request->get('referenceId'), 'transferId' => 0], 'message' => $this->response->message]);
        }
        return false;
    }

    public function getBalance(){

        if (!$this->token)
            return false;

        $this->apiAttr = CFConfiguration::$getBalance;
        $this->endpoint = $this->endpoint.$this->apiAttr['api'];
        $this->headers = ['Authorization: Bearer '.$this->token];
        if ($this->connect()){
            return $this->response;
        }
        return false;
    }

    public function directTransfer(){

        if (!$this->token)
            return false;

        $this->apiAttr = CFConfiguration::$directTransfer;
        $this->payload = [
                 "amount" => $this->request->get('amount'),
                 "transferId" => $this->request->get('transferId'),
                 "transferMode" => $this->request->get('transferMode') ?? 'neft',
                 "remarks" => $this->request->get('remarks'),
                 "beneDetails"  => [
                     "bankAccount" => $this->request->get('bankAccount'),
                     "ifsc" => $this->request->get('ifsc'),
                     "name" => $this->request->get('name'),
                     "email" => trim($this->request->get('email')),
                     "phone" => trim($this->request->get('phone')),
                     "address1" => $this->request->get('address1')
                 ]
        ];
        $this->endpoint = $this->endpoint.$this->apiAttr['api'];
        $this->headers = ['Authorization: Bearer '.$this->token];
        if ($this->connect()){
            if (!$this->isRefund) {
                $this->directTransferLog($this->request, $this->response);
            }
            return $this->response;
        }
        if (!$this->isRefund) {
            JobProvider::where('application_id', $this->request->get('id'))->where('from', 'InvokeCommunicationPayout')->update(['is_available' => 1]);
            $this->directTransferLog($this->request, $this->response);
        }
        return false;
    }


    public static function getSignature()
    {
        $clientId = _env('CF_X_CLIENT');
        $publicKey =
            openssl_pkey_get_public(file_get_contents("/var/www/certificate/cashfree-x-cf-sign-header.pem"));
        $encodedData = $clientId . "." . strtotime("now");
        return static::encrypt_RSA($encodedData, $publicKey);
    }

    private static function encrypt_RSA($plainData, $publicKey)
    {
        if (openssl_public_encrypt($plainData, $encrypted, $publicKey,
            OPENSSL_PKCS1_OAEP_PADDING))
            $encryptedData = base64_encode($encrypted);
        else return NULL;
        return $encryptedData;
    }


    /**
     * @return bool|string|null
     */
    private function connect(){
        $header = array_values(array_merge(array("Content-Type: application/json", "X-Client-Id: " . _env('CF_X_CLIENT'), "X-Client-Secret: " . _env('CF_X_CLIENT_SECRET'), "cache-control: no-cache", "X-Cf-Signature:".static::getSignature()),  $this->headers));
        $this->endpoint .= !$this->apiAttr['method'] ? '?'.http_build_query($this->payload) : '';
        $ch = curl_init($this->endpoint);
        if ($this->apiAttr['method']) {
            curl_setopt($ch, CURLOPT_POST, $this->apiAttr['method']);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->payload));
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $this->response = json_decode(curl_exec($ch));
        //(new Slack(new Request(['title' =>'API Response ', 'fields' => ['endpoint' => $this->endpoint, 'response' => json_encode($this->response)], 'group' => Slack::$STAGE_TESTING])))->send();
        $this->preserve();
        if (isset($this->response->status) && in_array($this->response->status,[CFConfiguration::$status, CFConfiguration::$statusPending]) && in_array($this->response->subCode, [CFConfiguration::$subCode, CFConfiguration::$subCodePending])){
            return $this->response;
        }else{
            $this->error = response()->json(['status'=> 'FAIL', 'message' => $this->response->message ?? 'Response received as null']);
        }
        return false;
    }

    /**
     * @return mixed
     */
    private function preserve(){
        $preserve = new CFPayoutPreserve();
        $preserve->fill([
            'api' => $this->endpoint,
            'request' => json_encode($this->payload),
            'response' => json_encode($this->response),
            'user_id' => Auth::check() ? Auth::user()->id : 107,
            'created_at' => Carbon::now()->toDateTimeString()
        ]);
        $preserve->save();
        return $preserve->id;
    }
}
