<?php

namespace App;

use App\Http\Controllers\ConsumerAPI\MessageBag;
use Firebase\JWT\JWT;
use Illuminate\Http\Request;

class JWTAccess extends MessageBag
{
    public $secret = "I'J~05SjU=S+nwc)UC`I@c^psP]ZgM&IaelEt>YC`U*1]Ag6nuXdu.YTy<:;,j";
    protected $user = null;
    private $defaultRouter = ['generate-otp','verify-otp'];

    public function decodeJWT(Request $request, $secret = null){

        $this->setSecret($secret, $request);

        $encodedString = JWT::decode($request->get('data'), $this->secret, array('HS512'));
        $_decryptHASMAC = $this->_decryptHASMAC($encodedString->data);

        //Ser session string for merchant api
        $_decryptHASMAC = $this->setSessionStringMerchantAPI($_decryptHASMAC);

        return (object)$_decryptHASMAC;
    }

    public function decodeJWTV1(Request $request, $secret = null){

        $this->setSecret($this->secret, $request);

        $encodedString = JWT::decode($request->get('data'), $this->secret, array('HS512'));
        return (object) $encodedString;
    }

    public function encodeJWTV1(Request $request,  $secret = null){

        $this->setSecret($secret, $request);

        //$tokenId    = base64_encode('121211212');
        $issuedAt   = time();
        $notBefore  = $issuedAt - 10; // + 10 remove, add commented code - testing
        $expire     = $issuedAt + (60 * 60 * 24 * 30); //60 or (60 * 4)
        //$serverName = $request->getHttpHost();

        $data = [
            'iat'  => $issuedAt,
            'nbf'  => $notBefore,
            'exp'  => $expire,
            'id' => $request->get('bot_app_id')
        ];

        $jwt = JWT::encode($data, $this->secret, 'HS256');

        return $jwt;
    }

    public function encodeJWT(Request $request,  $secret = null){

        $this->setSecret($secret, $request);

        $tokenId    = base64_encode('121211212');
        $issuedAt   = time();
        $notBefore  = $issuedAt - 10; // + 10 remove, add commented code - testing
        $expire     = isset($request->get('data')['jte_iv_eop']) ? $issuedAt + (int)$request->get('data')['jte_iv_eop'] : $issuedAt + (60 * 60 * 24 * 30); //60 or (60 * 4)
        $serverName = $request->getHttpHost();

        $data = [
            'iat'  => $issuedAt,
            'jti'  => $tokenId,
            'iss'  => $serverName,
            'nbf'  => $notBefore,
            'exp'  => $expire,
            'data' => $this->_encryptHASMAC(json_encode($request->get('data')))
        ];

        $jwt = JWT::encode($data, $this->secret, 'HS512');

        return $jwt;
    }

    public function encodeJWTDecisionEngine(Request $request,  $secret = null){

        $this->setSecret($secret, $request);

        $issuedAt   = time();
        $notBefore  = $issuedAt - 10; // + 10 remove, add commented code - testing
        $expire     = $issuedAt + (60 * 60 * 24 * 30); //60 or (60 * 4)

        $data = [
            'iat'  => $issuedAt,
            'nbf'  => $notBefore,
            'exp'  => $expire,
            'id' => $request->get('application_id')
        ];

        $jwt = JWT::encode($data, $this->secret, 'HS512');

        return $jwt;
    }

    public function verifyAccessToken(Request $request){
        $request = new Request(['data' => $this->getAuthorizationToken($request)]);
        $user = $this->decodeJWT($request, $this->secret);
        $this->user = isset($user->id) && !$this->user ? $this->getUser($user) : true;
        return $this->user;
    }

    public function verifyAccessTokenV1(Request  $request){
        $request = new Request(['data' => $this->getAuthorizationToken($request)]);
        $user = $this->decodeJWTV1($request, $this->secret);
        $this->user = isset($user->id) && !$this->user ? $this->getUser($user) : true;
        return $this->user;
    }

    public function generateAccessToken($user){
        return $this->encodeJWT(new Request(['data' => $this->getUser($user)]),  $this->secret);
    }

    private function getUser($userData){
        $user = User::find($userData->id);
        if ($user) {
            return ['id' => $user->id, 'name' => $user->name, 'user_type' => $user->user_type, 'code' => $user->code, 'remember_token' => $user->remember_token, 'email' => $user->email, 'center_id' => $user->center_id, 'mobile_number' => $user->mobile, 'session_string' => $userData->session_string ?? ''];
        }
        return array();
    }

    public function setSecret($secret, Request $request){
        $this->secret = $secret || in_array($request->route()->uri(), $this->defaultRouter) ? ($secret) : $this->getAuthorizationToken($request);
    }

    public function getAuthorizationToken(Request $request){
        return $request->get('Authorization') ? $request->get('Authorization') : str_replace('Bearer ', "" , $request->header('Authorization'));
    }

    public function _decryptHASMAC($encrypted){
        $encryptedEncrypted = json_decode($encrypted, true);
        $salt = hex2bin($encryptedEncrypted["s"]);
        $ct = base64_decode($encryptedEncrypted["ct"]);
        $iv  = hex2bin($encryptedEncrypted["iv"]);
        $concatPassphrase = $this->secret.$salt;
        $md5 = array();
        $md5[0] = md5($concatPassphrase, true);
        $result = $md5[0];
        for ($i = 1; $i < 3; $i++) {
            $md5[$i] = md5($md5[$i - 1].$concatPassphrase, true);
            $result .= $md5[$i];
        }
        $key = substr($result, 0, 32);
        $data = openssl_decrypt($ct, 'aes-256-cbc', $key, true, $iv);
        return json_decode($data, true);
    }

    public function _encryptHASMAC($decrypted){
        $salt = openssl_random_pseudo_bytes(8);
        $salted = '';
        $dx = '';
        while (strlen($salted) < 48) {
            $dx = md5($dx.$this->secret.$salt, true);
            $salted .= $dx;
        }
        $key = substr($salted, 0, 32);
        $iv  = substr($salted, 32,16);
        $encrypted_data = openssl_encrypt($decrypted, 'aes-256-cbc', $key, true, $iv);
        $data = array("ct" => base64_encode($encrypted_data), "iv" => bin2hex($iv), "s" => bin2hex($salt));
        return json_encode($data);
    }

    public function setSessionStringMerchantAPI($_decryptHASMAC){
        if (!isset($_decryptHASMAC['session_string']))
            $_decryptHASMAC['session_string'] = $this->user && isset($this->user['session_string']) ? $this->user['session_string'] : '';
        return $_decryptHASMAC;
    }
}
