<?php
/* Biblioteca de integração com a API da Pagseguro */

class PagseguroAPI extends RestApi
{
    const cryptoKey = 'bWVsaG9yIGVudmlvIGF1dGVudGljYWNhbw==';

    const urlCallback = 'https://efutura.futurasistemas.com.br/pagseguro/pagsegurocallback.php';

    // Teste (Sandbox)
    // A criação da aplicacao de Sandbox e as credenciais client_id e client_secret foram obtidas via endpoint realizando requisicao no Postman.
    // Endpoint: https://sandbox.api.pagseguro.com/oauth2/application
    const urlPagseguroSandbox = 'https://sandbox.api.pagseguro.com';
    const urlPagseguroAuthSandbox = 'https://connect.sandbox.pagseguro.uol.com.br';
    const clientIdFuturaSandbox = '41b56935-4e6f-4eb2-870d-d641b72d162f';
    const clientSecretFuturaSandbox = '87e38302-4a42-4c9f-9252-327439527c3e';
    const tokenApplicationFuturaSandbox = 'ECFBB3F46DAD493FAE3BD9EEC1726973';
    const publicKeySandbox = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr+ZqgD892U9/HXsa7XqBZUayPquAfh9xx4iwUbTSUAvTlmiXFQNTp0Bvt/5vK2FhMj39qSv1zi2OuBjvW38q1E374nzx6NNBL5JosV0+SDINTlCG0cmigHuBOyWzYmjgca+mtQu4WczCaApNaSuVqgb8u7Bd9GCOL4YJotvV5+81frlSwQXralhwRzGhj/A57CGPgGKiuPT+AOGmykIGEZsSD9RKkyoKIoc0OS8CPIzdBOtTQCIwrLn2FxI83Clcg55W8gkFSOS6rWNbG5qFZWMll6yl02HtunalHmUlRUL66YeGXdMDC2PuRcmZbGO5a/2tbVppW6mfSWG3NPRpgwIDAQAB';

    // Produção
    const urlPagseguroProduction = 'https://api.pagseguro.com';
    const urlPagseguroAuthProduction = 'https://connect.pagseguro.uol.com.br';
    const clientIdFutura = '7104e989-fabf-45eb-938d-bbc94e7a7111';
    const clientSecretFutura = 'a9138eaf-752c-4f56-9a6a-2b984f8ff9dc';
    const tokenApplicationFutura = '1B9BFD5C89FE469DACB818A9274ED7CF';
    const publicKeyFutura = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2A5Vbb3lM7S3UUiy50VIdcw3pWfIJnpSQDJ7PmUFYLLA5hk4Bwi9tB+7R7k5R81SWXRidthAZEEef4FT+jZOKSNu0oCjcWiOo9V18X5+RMJhYR/1Xl016W9Eb6WJ8TvAmqI4huVEJInP/p8M712kKrWFqQm3/EWZJpgG0bZHbHYbv3BNCQmbWs32WiFJP5eypgOSfWc5DYUgnKfOcNUUFG6p48Iv//yzz2GOsesr35jPBW6f7+pdxwfxJF8X5Cf5ihsCjqrfeiP3/DTTrd8FhUZpMEiJk5qwp60BUaL6lENXZD+iHkv59Bt+3PCeTcSn0YkkGszK1gJIjh8gXfHt8wIDAQAB';
    // client id e secret de produção ficam na oc_setting quando instala o módulo

    private $header;

    private $accessToken = null;
    private $refreshToken;
    private $tokenExpire;

    private $environment;
    private $environmentAuth;
    private $urlCallback;
    private $token;
    private $appId;
    private $appSecret;
    private $registry;
    private $load;
    private $config;
    private $session;
    private $db;
    private $log;
    private $url;

    public function __construct($registry)
    {
        $this->registry = $registry;
        $this->load = $registry->get('load');
        $this->config = $registry->get('config');
        $this->session = $registry->get('session');
        $this->db = $registry->get('db');
        $this->log = $registry->get('log');
        $this->url = $registry->get('url');

        $this->curlOpts = array(CURLOPT_ENCODING => '');

        $this->setHeader();
        $this->setEnvironment();

        if ($this->config->get('pagseguro_api_ambiente') == Type_Environment::Tp_Production) {
            $this->token = trim($this->config->get('pagseguro_api_token'));
        } else {
            $this->token = trim($this->config->get('pagseguro_api_token_sandbox'));
        }
        $this->refreshToken = trim($this->config->get('pagseguro_api_refresh_token'));
        $this->accessToken = trim($this->config->get('pagseguro_api_access_token'));
        $this->tokenExpire = trim($this->config->get('pagseguro_api_token_expire'));
    }

    public function __get($name){
        return $this->registry->get($name);
    }

    public function setEnvironment()
    {
        $this->urlCallback = self::urlCallback;
        if ($this->config->get('pagseguro_api_ambiente') == Type_Environment::Tp_Production) {
            $this->environment = self::urlPagseguroProduction;
            $this->environmentAuth = self::urlPagseguroAuthProduction;
            $this->appId = trim($this->config->get('pagseguro_api_client_id'));
            $this->appSecret = trim($this->config->get('pagseguro_api_client_secret'));
        } else {
            $this->environment = self::urlPagseguroSandbox;
            $this->environmentAuth = self::urlPagseguroAuthSandbox;
            $this->appId = trim($this->config->get('pagseguro_api_sandbox_client_id'));
            $this->appSecret = trim($this->config->get('pagseguro_api_sandbox_client_secret'));
        }
    }

    public function getEnvironment(){
        return $this->environment;
    }

    public function getEnvironmentAuth(){
        return $this->environmentAuth;
    }

    public function setHeader(){
        $this->header = array(
            "Accept: application/json",
            "Content-Type: application/json",
        );
    }

    public function getHeader(){
        return $this->header;
    }

    public function getLoginUrl()
    {
        $urlCallbackLoja = $this->urlloja->link('', '', 'NONSSL', false) . 'pagseguroapiauth';
        $url = $this->environmentAuth . '/oauth2/authorize'
            . '?response_type=code'
            . '&client_id=' . $this->appId
            . '&redirect_uri=' . urlencode($this->urlCallback)
            . '&scope=payments.read+payments.create+payments.refund+accounts.read'
            . '&state=' . ftr_geral::base64url_encode($urlCallbackLoja);

        $this->log->write('URL: ' . json_encode($url, JSON_UNESCAPED_UNICODE));
        return $url;
    }

    public function getRefreshToken($code)
    {
        try {
            $url = $this->getEnvironment() . '/oauth2/token';

            $header = [
                "Authorization: Bearer " . $this->token,
                "Content-Type: application/json",
                "X_CLIENT_ID: " . $this->appId,
                "X_CLIENT_SECRET: " . $this->appSecret,
            ];

            $param = [
                "grant_type" => "authorization_code",
                "code" => $code,
                "redirect_uri" => $this->urlCallback,
            ];

            $param = json_encode($param);

            $data = parent::callAPI($url, 'POST', $param, $header, false, $retorno);

            $this->log->write('URL: ' .json_encode($url, JSON_UNESCAPED_UNICODE));
            $this->log->write('Header: ' . json_encode($header, JSON_UNESCAPED_UNICODE));
            $this->log->write('Param: ' . $param);
            $this->log->write('Retorno: ' . json_encode($retorno, JSON_UNESCAPED_UNICODE));
            $this->log->write('Data: ' . json_encode($data, JSON_UNESCAPED_UNICODE));

            if (is_object($data) && property_exists($data, 'access_token')) {
                $this->log->write('Data property: ' . json_encode($data, JSON_UNESCAPED_UNICODE));
                $accessToken = $data->access_token;
                $tokenExpire = $data->expires_in;
                $refreshToken = $data->refresh_token;

                //Formatar tokenExpire de segundos para data padrão DD/MM/YYYY;
                $dateExpire = new DateTime();
                $dateExpire->add(new DateInterval('PT' . $tokenExpire. 'S'));
                $accesTokenExpire = $dateExpire->format('Y-m-d H:i:s');

                $this->log->write('Refresh Token: ' . json_encode($refreshToken, JSON_UNESCAPED_UNICODE));
                $this->setAccess($accessToken, $refreshToken, $accesTokenExpire);
            } else {
                $msgError = 'Erro ao gerar token de acesso. Código HTTP: ' . $retorno . '. Erro da API:  ';
                $this->setErrorLog($msgError, json_encode($data, JSON_UNESCAPED_UNICODE));
                throw new Exception();
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    public function getAccessToken($client_id = false, $client_secret = false, $token = false, $environment = false){
        
        $client_id = $this->appId;     
        $client_secret = $this->appSecret;

        $token = ($token) ? $token : $this->token;
        $environment = ($environment) ? ($environment == Type_Environment::Tp_Sandbox ? self::urlPagseguroSandbox : self::urlPagseguroProduction) : $this->environment;

        $url = $environment. '/oauth2/refresh';

        $header = [
            "Authorization: Bearer " . $token,
            "Content-Type: application/json",
            "X_CLIENT_ID: " . $client_id,
            "X_CLIENT_SECRET: " . $client_secret,
        ];

        $this->log->write('Header Access Token: ' . json_encode($header, JSON_UNESCAPED_UNICODE));

        $param = array(
            "grant_type" => "refresh_token",
            "refresh_token" => $this->refreshToken ?? ''
        );

        $param = json_encode($param);

        $data = parent::callAPI($url, 'POST', $param, $header, false, $retorno);

        $this->log->write('Data Access Token: ' . json_encode($data, JSON_UNESCAPED_UNICODE));

        if (is_object($data) && property_exists($data, 'access_token')) {
            $dateExpire = new DateTime();
            $dateExpire->add(new DateInterval('PT' . $data->expires_in . 'S'));

            $this->setAccess($data->access_token, $data->refresh_token, $dateExpire->format('Y-m-d H:i:s'));
        }

        return $data;
    }

    public function setAccess($accessToken, $refreshToken, $tokenExpire){
        $this->load->model('setting/setting');

        $data = array(
            'pagseguro_api_access_token'  => $accessToken,
            'pagseguro_api_refresh_token' => $refreshToken,
            'pagseguro_api_token_expire'  => $tokenExpire,
        );

        $this->model_setting_setting->editSettingForKey('pagseguro_api', $data);

        $this->accessToken  = $accessToken;
        $this->refreshToken = $refreshToken;
        $this->tokenExpire  = $tokenExpire;
    }

    public function getAccess(){
        if ($this->config->get('pagseguro_api_status') == Type_Status::Tp_Habilitado) {
            $dateNow = new DateTime();
            $dateNow->add(new DateInterval('PT1M'));

            $dateExpire = DateTime::createFromFormat('Y-m-d H:i:s', $this->tokenExpire);

            // Se não tem access token ou está expirado, pega um novo. Senão já está setado no construct
            if ((!$this->accessToken) || ($dateNow > $dateExpire)) {
                $this->getAccessToken();
           }
        }
    }

    public function validateAccess(){
        // gambiarra para verificar se a autenticação está correta, se tivesse um endpoint pra isso não precisaria buscar uma order com id 0
        $url = $this->environment . '/orders/0';

        $header = [
            "Authorization: Bearer " . $this->accessToken,
            "Content-Type: application/json",
        ];

        $data = parent::callAPI($url, 'GET', false, $header, false, $retorno);

        // se retorna pedido não encontrado significa que a autorização esta funcionando
        return $retorno == '404';
    }

    public function sendPayment($dataSend, $order_id){
        try {
            $this->getAccess();
            $header = [
                'Content-Type: application/json',
                'Authorization: '.$this->accessToken
            ];

            $url = $this->environment . '/orders';

            $data = parent::callAPI($url, 'POST', $dataSend, $header, false, $retorno, Type_DadosSend::TpDados_Json);

            $this->log->write(json_encode($url, JSON_UNESCAPED_UNICODE));
            $this->log->write(json_encode($dataSend, JSON_UNESCAPED_UNICODE));
            $this->log->write(json_encode($header, JSON_UNESCAPED_UNICODE));
            $this->log->write(json_encode($retorno, JSON_UNESCAPED_UNICODE));
            $this->log->write(json_encode($data, JSON_UNESCAPED_UNICODE));

            if($retorno == '201'){
            // Logs para homologação
//                $logRequest = 'Código HTTP: '.$retorno.PHP_EOL.
//                         'Header: '.json_encode($header).PHP_EOL.
//                         'Request: '.json_encode($dataSend).PHP_EOL.
//                         'Response: '.json_encode($data);
//                $this->log->write($logRequest);
                return $data;
            } else {
                $msgError = 'Erro ao finalizar o pedido '.$order_id.' usando pagamento Pagseguro. Código HTTP: '.$retorno.'. Erro da API:  ';
                $this->setErrorLog($msgError, $data);
                throw new exception($msgError);
            }

        } catch (Exception $e) {
            throw $e;
        }
    }

    public function cancelPayment($order_id, $order_codigo_integracao_pagamento, $value){
        try {
            $this->getAccess();
            $header = [
                'Content-Type: application/json',
                'x-api-version: 4.0',
                'Authorization: '.$this->accessToken,
            ];

            $url = $this->environment . '/charges/' . $order_codigo_integracao_pagamento . '/cancel';

            $valueObj = (Object)['value' => ((Integer)($value*100))];
            $dataSend = (Object)['amount' => $valueObj];

            $data = parent::callAPI($url, 'POST', $dataSend, $header, false, $retorno, Type_DadosSend::TpDados_Json);

//               Logs para homologação
//                 $logRequest = 'Código HTTP: '.$retorno.PHP_EOL.
//                      'Header: '.json_encode($header).PHP_EOL.
//                      'Request: '.json_encode($dataSend).PHP_EOL.
//                      'Response: '.json_encode($data);
//                 $this->log->write($logRequest);


            if ($retorno == '201'){
                return true;
            } else {
                $msgError = 'Erro ao finalizar o pedido '.$order_id.' usando pagamento Pagseguro. Código HTTP: '.$retorno.'. Erro da API:  ';
                $this->setErrorLog($msgError, $data);
                if (isset($data->error_messages)){
                    return current($data->error_messages)->description ?? current($data->error_messages)->message;
                } else {
                    return false;
                }
            }
        } catch (Exception $e) {
            throw $e;
        }
    }

    protected function setErrorLog($msgError, $data){
        if ($this->config->get('pagseguro_api_log_error')){
            if (isset($data->error_messages)){
                $this->log->write($msgError.PHP_EOL.print_r($data->error_messages,true));
            }
        }
    }

}
