<?php

class Matera extends RestApi
{
  private $urlFutura              = 'http://grogumateraapi.futurasistemas.com.br/api/v2/';
  private $urlFuturaToken         = 'http://grogumateraapi.futurasistemas.com.br/api/v1/';
  private $urlMateraPRD           = 'https://mtls-mp.prd.flagship.maas.link/';
  private $urlMateraHML           = 'https://mtls-mp.hml.flagship.maas.link/';
  private $certPem                = __DIR__ . "/matera/certificate.pem";
  private $keyPem                 = __DIR__ . "/matera/private_key.key";  

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

  public function getAccessToken(){

    $header = [
      "Content-type: application/json; charset=utf-8",
      "Cache-Control: no-cache"
    ];

    try{   
      $param_efutura = $this->getParamEfuturaDb(); 
  
      $key = Crypto::descriptografarAES256($param_efutura['token_matera'], Crypto::CAES256);

      [$username, $password] = explode('&', $key);

      $url = "{$this->urlFuturaToken}sys/login?username=$username&password=$password";

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

      $token = $response->access_token ?? null;

      return $token;

    } catch(Exception $e){
      throw new Exception('Erro ao gerar token Matera: '. $e->getMessage());
    };
  }

  public function getAccount($data = null){

    $token = $this->getAccessToken();

    $header = [
      "Content-type: application/json; charset=utf-8",
      "Authorization: bearer $token",
      "Cache-Control: no-cache"
    ];

    try { 
      $cnpj = $data['cnpj'] ?? preg_replace('/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/', '$1.$2.$3/$4-$5', $data); 
      
      $response = parent::callAPI("{$this->urlFutura}config/full_account?cnpj=$cnpj&modulo_tipo=2", 'GET', false, $header, false, $retorno, Type_DadosSend::TpDados_Json);
  
      return $response;

    } catch(Exception $e) {
      throw new Exception('Erro ao buscar cliente: '. $e->getMessage());
    };
  }

  public function sendPaymentPix($paymentData, $paymentInfo){

    $environment = $this->config->get('matera_api_ambiente') ?? 'sandbox';

    $urlEnvironment = $environment == 'sandbox' ? $this->urlMateraHML : $this->urlMateraPRD; 

    $client = $this->getClientDb();

    if($client) {
      $accountClient = $this->getAccount($client['cnpj']);

      $result = $this->formatFullAccount($accountClient);
      
      $accountInfo = $result['accountInfo'];
    
      $token = $this->getAuthMatera($accountInfo);

      $transactionHash = $this->getTransactionHash($accountInfo, $paymentInfo);

      $order = $this->createOrder($paymentData ,$paymentInfo, $accountClient,'Pix');

      $header = [
        "Content-type: application/json",
        "Cache-Control: no-cache",
        "Authorization: Bearer $token",
        "Content-Length: " . strlen(json_encode($order)),
        "Transaction-Hash: $transactionHash", 
        "Host: " . parse_url($urlEnvironment, PHP_URL_HOST)
      ];

      $auth = [
        "certificate" => $this->certPem, 
        "key" => $this->keyPem
      ];

      $data = parent::callAPI("{$urlEnvironment}v1/payments" , 'POST', $order, $header, $auth, $retorno, Type_DadosSend::TpDados_Json);

      if($data){
        switch ($retorno) {
          case '500':
          case '200':
          case '201':
            return $data;
          default :
            $error = 'Erro no módulo de pagamento';
            $error .= 'Mensagem: Erro na conexão com a Matera';
            throw new Exception($error);
        }
      } else {
        $msgError = "Erro ao finalizar o pedido usando Futura Pix. Erro da API: $data->message";
        throw new Exception($msgError);
      }
    }
  }

  private function createOrder($paymentData, $paymentInfo, $accountClient, $method){
    $this->load->type('tipomatera');

    $result = $this->formatFullAccount($accountClient);
    
    $externalIdentifier = $this->getExternalIdentifier();

    if($result) {
      $accountInfo = $result['accountInfo'];
      $transaction = $result['transaction'];
    
      $payload = [];
      $expireQrcode = $this->config->get('matera_pix');
      $urlCallback = $this->getCallbackUrl($accountInfo->cnpj);

      $data['currency'] = Type_CodigoMoeda::Tp_MoedReal;
      $data['totalAmount'] = floatval($paymentInfo->getParam('amount'));
      $data['externalIdentifier'] = "E*$externalIdentifier";
      $data['callbackAddress'] = $urlCallback;

      $payment_info['transactionType'] = Type_materaTransactionType::Tp_InstantPayment;
      $payment_info['instantPayment']['expiration'] = (int)$expireQrcode['expires_at']  * 24 * 60 * 60;
      $payment_info['instantPayment']['alias'] = $accountInfo->chave_pix;

      $qrCode['errorCorrectionLevel'] = 'M';                              
      $qrCode['imageWidth' ] = 400; 
      $qrCode['generateImageRendering'] = false;
      $payment_info['instantPayment']['qrCodeImageGenerationSpecification'] = $qrCode;

      $recipients['account']['accountId'] = $accountInfo->account_id;
      $recipients['amount'] = floatval($paymentInfo->getParam('amount'));
      $recipients['currency'] = Type_CodigoMoeda::Tp_MoedReal;
      $recipients['mediatorFee'] = $paymentData['paymentMediatorFee']['mediatorFee'];
      $recipients['senderComment'] = "";

      $payload = $data;
      $payload['paymentInfo'] = $payment_info;
      $payload['recipients'][] = $recipients;
    
      return $payload;
    }
  }

  private function formatFullAccount($data = null){

    $accountFounded = null;
    $billingAddressInfo = [];
    $transaction = [];
    $accountInfo = $data->conta;

    foreach ($data->transacao as $item) {
      if ($item->mensagem === "CONTA CADASTRADA") {
        $accountFounded = $item;
        break;
      };
    };

    if ($accountFounded) {
      $transaction = json_decode($accountFounded->json_envio, true) ?? [];
      $billingAddressInfo = $transaction['billingAddress'] ?? [];
    };
    
    $fullAccount = [
      'accountInfo' => $accountInfo,
      'transaction' => $transaction,
      'billingAddressInfo' => $billingAddressInfo
    ];

    return $fullAccount;
  }

  private function getCallbackUrl($cnpj = null){

    $urlApi = "{$this->urlFutura}callback/callbackEcommerce/callbackData";

    $urlClient = $this->url->link('payment/matera/callback');

    $url = "$urlClient|$cnpj";

    $urlEncode =  ftr_geral::base64url_encode($url);

    $urlCallback = "$urlApi?client=$urlEncode";

    return $urlClient;
  }

  private function getAuthMatera($data = null){

    $environment = $this->config->get('matera_api_ambiente') ?? 'sandbox';

    $urlEnvironment = $environment == 'sandbox' ? $this->urlMateraHML : $this->urlMateraPRD; 

    $client_id = Crypto::descriptografarAES256($data->client_id, Crypto::CAES256);

    $client_secret = Crypto::descriptografarAES256($data->client_secret, Crypto::CAES256);

    $url = "{$urlEnvironment}auth/realms/Matera/protocol/openid-connect/token";

    $body = http_build_query([
      'grant_type' => 'client_credentials',
      'client_id'  => $client_id,
      'client_secret' => $client_secret,
    ]);

    $curl = curl_init();

    curl_setopt_array($curl, [
        CURLOPT_URL => $url,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/x-www-form-urlencoded',
            'Cache-Control: no-cache',
            'Content-Length: ' . strlen($body),
            'Host: ' . parse_url($urlEnvironment, PHP_URL_HOST)
        ],
        CURLOPT_SSLCERT => $this->certPem,
        CURLOPT_SSLKEY  => $this->keyPem,
    ]);

    $response = curl_exec($curl);
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    $error = curl_errno($curl) ? curl_error($curl) : null;

    curl_close($curl);

    if ($error) {
      throw new Exception("Erro cURL: {$error}");
    }

    if ($httpCode === 200) {
      $responseData = json_decode($response, true);

      if (!isset($responseData['access_token'])) {
        throw new Exception("Erro na autenticação: token não encontrado na resposta.");
      }

      $token = $responseData['access_token'];
      $expiresIn = $responseData['expires_in'] ?? 0;
      $validadeToken = (new DateTime())->modify("+{$expiresIn} seconds");

      return $token;

    } else {
      throw new Exception("Erro HTTP: Código {$httpCode}. Resposta: {$response}");
    }
  }

  private function getClientDb(){

    $result = $this->db->query("select * from oc_matera_config");

    $client = $result->row;

    return $client;
  }

  private function getParamEfuturaDb(){

    $result = $this->db->query("select * from parametros_efutura");
     
    $param_efutura = $result->row; 

    return $param_efutura;
  }

  private function getTransactionHash($accountInfo = null, $paymentInfo = null){

    $alias = $accountInfo->chave_pix; 
    $totalAmount = floor($paymentInfo->getParam('amount'));
    $accountId = $accountInfo->account_id;
    $secretKey = $accountInfo->secret_key; 

    $secretKeyDescript = Crypto::descriptografarAES256($secretKey, Crypto::CAES256);

    $hashBase = "{$alias}{$totalAmount}{$accountId}{$totalAmount}"; 
  
    $transactionHash = hash_hmac('sha256', $hashBase, $secretKeyDescript);
   
    return $transactionHash;
  }

  private function getExternalIdentifier(){

    $guid = bin2hex(random_bytes(16));

    $guidFormat = str_replace('-', '', $guid);
   
    return strtoupper(substr($guidFormat, 0, 32));
  }
}

