<?php
$loader = $this->registry->get('load');
$loader->controller('payment/clearsale');
$loader->controller('payment/payment_info');

class ControllerPaymentCieloApiPix extends ControllerPaymentPaymentInfo
{
    private $dadosTransacao;

    public function pix()
    {
        parent::__construct($this->registry);

        $language = $this->language->load('payment/cielo_api_pix');
        $this->data = array_merge($this->data, $language);

        $this->load->type('tipocieloapi');
        $this->load->type('tipocomum');

        if (isset($this->session->data['order_id'])) {
            $this->data['idorder'] = $this->session->data['order_id'];
        }

        if (isset($this->session->data['payment_method']['code_type']) && isset($this->session->data['payment_method']['code'])) {
            $code_type = $this->session->data['payment_method']['code_type'];
            $code = $this->session->data['payment_method']['code'];

            $this->data['paymentRoute'] = "$code/pay_$code_type";
            $this->data['paymentName'] = $code;
        }

        $this->data['ambiente_teste'] = $this->config->get('cielo_api_ambiente_teste');
        $this->data['captcha_tpl'] = $this->getChild('payment/captcha');

        $setting = [
            'display_cpf' => true,
            'payment_name' => Type_CieloApiPixConfigs::Tp_NameModule,
            'load_customer_info' => true,
            'display_titular' => false
        ];

        $this->data['payment_address'] = $this->getChild('payment/payment_address', $setting);

        if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/payment/' . Type_CieloApiPixConfigs::Tp_NameModule . '/pix.twig')) {
            $this->template = $this->config->get('config_template') . '/template/payment/' . Type_CieloApiPixConfigs::Tp_NameModule . '/pix.twig';
        } else {
            $this->template = 'default/template/payment/pix.twig';
        }

        $this->response->setOutput($this->render('payment/' . Type_CieloApiPixConfigs::Tp_NameModule . '/pix'));
    }

    public function pay_pix()
    {
        $json = array();

        $this->load->language('payment/cielo_api_pix');
        $this->load->type('tipocieloapi');
        $this->load->type('tipocomum');
        $this->load->helper('Ftr_sql');

        $json['error'] = $this->validate();
        if (!$json['error']) {
            ini_set("allow_url_fopen", 1);
           
            $this->load->dmodel('checkout/order');
            $this->load->dmodel('payment/payment_info');

            $cieloorderID = $this->dmodel_payment_payment_info->getPaymentInfo($this->session->data['order_id'], Type_PagamentoTabela::Tp_Pix, true);
            if (!$cieloorderID) {//validação para não deixar duplicar o pagamento
               
                $order_info = $this->getOrder($this->session->data['order_id']);

                $valor = $order_info['total'];

                $vvalorPedido = number_format($valor, 2, '.', '');//Cielo requisita o valor em centavos
                $compratotal = number_format($vvalorPedido, 2, '', '');

                $tipoDoc = empty($order_info['cnpj']) ? 'CPF' : 'CNPJ';
                $document = $tipoDoc == 'CPF' ? preg_replace('/\D/', '', $order_info['cpf']) : preg_replace('/\D/', '', $order_info['cnpj']);

                $this->dadosTransacao = array(
                    'id_pedido' => $this->session->data['order_id'],
                    'valor' => $compratotal
                );

                $this->dadosTransacao['custommer'] = array(
                    'nome_cliente' => $order_info['payment_firstname'] . " " . $order_info['payment_lastname'],
                    'email_cliente' => $order_info['email'],
                    'documento' => $document,
                    'tipo_documento' => $tipoDoc
                );

                $retornoTransacao = $this->cieloapi->cieloPagamentoPix($this->dadosTransacao);
                $this->log->write("Retorno " . json_encode($retornoTransacao));
                if ($retornoTransacao && isset($retornoTransacao->Payment)) {
                    if ((!property_exists($retornoTransacao->Payment, 'QrCodeBase64Image') || empty($retornoTransacao->Payment->QrCodeBase64Image))
                        || (!property_exists($retornoTransacao->Payment, 'QrCodeString') || empty($retornoTransacao->Payment->QrCodeString)) 
                        || (!property_exists($retornoTransacao->Payment, 'PaymentId') || empty($retornoTransacao->Payment->PaymentId)) 
                        || (!property_exists($retornoTransacao->Payment, 'Status') || empty($retornoTransacao->Payment->Status)) 
                        || (!property_exists($retornoTransacao->Payment, 'SentOrderId') || empty($retornoTransacao->Payment->SentOrderId)))
                    {
                        $retornoTransacao->Payment->Status = Type_CieloApiPixStatus::Tp_NotFinished;
                         $json['error']['retorno_api'] = array(
                            "descricao" => "Erro ao gerar o qr code, dados de retorno incompletos",
                            "mensagem"  => $this->language->get('text_erro_retorno')
                        );
                    }

                    $PPqrCodeId = $retornoTransacao->Payment->PaymentId ?? ($retornoTransacao->Payment->Tid ?? $retornoTransacao->Payment->SentOrderId);
                    $PPqrCode = $retornoTransacao->Payment->QrCodeString;
                    $PPurl = $retornoTransacao->Payment->QrCodeBase64Image;

                    $sqlUpdParam = Ftr_sql::generateUpdate('oc_order', ['codigo_integracao_pagamento'], 'order_id = ' . $order_info['order_id']);
                    try {
                        $this->db->executeSql($sqlUpdParam, [$PPqrCodeId]);
                    } catch (\Throwable $e) {
                        $this->log->write('Erro ao salvar o código de integração do pedido ' . $order_info['order_id'] . ' no banco de dados. Error: ' . $e->getMessage());
                    }

                    switch ($retornoTransacao->Payment->Status) {
                        case Type_CieloApiPixStatus::Tp_Pending:
                        case Type_CieloApiPixStatus::Tp_Authorized:
                            $status_pagamento = $this->config->get('cielo_api_pix_order_status_created');
                            break;
                        case Type_CieloApiPixStatus::Tp_NotFinished:
                            $status_pagamento = $this->config->get('cielo_api_pix_order_status_rejected');
                            break;
                        case Type_CieloApiPixStatus::Tp_Aborted:
                        case Type_CieloApiPixStatus::Tp_Refunded:
                            $status_pagamento = $this->config->get('cielo_api_pix_order_status_cancelado');
                            break;
                    }

                    $pixDate = strtotime($retornoTransacao->Payment->ReceivedDate ?? date('Y-m-d H:i:s'));
                    $paymentInfo = [];
                    $paymentInfo['order_id'] = $order_info['order_id'];
                    $paymentInfo['payment_id'] = $PPqrCodeId;
                    $paymentInfo['data_criacao'] = date('Y-m-d H:i:s', $pixDate);
                    $paymentInfo['data_vencimento'] = date('Y-m-d H:i:s', $pixDate + ($retornoTransacao->Payment->QrCode->Expiration ?? 3600)); // Adiciona o tempo de expiração ao timestamp
                    $paymentInfo['status_pagamento'] = $status_pagamento;//verificar como tratar, pois aqui volta texto!
                    $paymentInfo['link_qrcodeurl'] = $PPurl;
                    $paymentInfo['link_qrcode'] = $PPqrCode;

                    $this->load->dmodel('payment/payment_info');
                    try {
                        $sqlUpdParam = Ftr_sql::generateUpdate('oc_order', ['order_status_id'], 'order_id = ' . $order_info['order_id']);
                        $this->db->executeSql($sqlUpdParam, [$status_pagamento]);
                        $this->dmodel_payment_payment_info->addPaymentPix($paymentInfo);
                    } catch (\Throwable $e) {
                        $this->log->write('Erro ao adicionar informações de pagamento PIX para o pedido ' . $order_info['order_id'] . '. Error: ' . $e->getMessage());
                    }
                } else {
                    $this->response->addHeader('HTTP/1.1 400 Bad Request');
                    $json['error'] = 'Parâmetros valor, minutos e msg são obrigatórios ou a classe CIELO PIX não foi inicializada.';
                    $this->log->write('Erro: ' . $json['error'] . ' Pedido: ' . $order_info['order_id']);
                }
            } else {
                $this->log->write('Erro inesperado durante o processamento do pagamento PIX para o pedido ' . ($this->session->data['order_id'] ?? 'desconhecido'));
                $this->dmodel_checkout_order->update($this->session->data['order_id'], $this->config->get('cielo_api_pix_order_status_canceled'), 'Erro ao processar pagamento PIX');
                $json['error'] = 'Ocorreu um erro ao processar seu pagamento. Por favor, tente novamente.';
            }

            $json['redirect'] = $this->url->link('checkout/success');
        }

        $this->response->setOutput(json_encode($json));
    }

    public function success()
    {
        $this->session->data['payment_session']['code'] = 'cielo_api_pix';
        $this->session->data['payment_session']['code_type'] = 'pix';
        $this->session->data['payment_session']['title'] = 'Cielo Pix';
        if (isset($this->session->data['payment_session']['code']) && ($this->session->data['payment_session']['code'] == Type_CieloApiPixConfigs::Tp_NameModule)) {

        $this->load->dmodel('payment/payment_info');
        $this->load->language('payment/' . $this->session->data['payment_session']['code']);

        switch ($this->session->data['payment_session']['code_type']) {
            case Type_PaymentOption::Tp_Pix:
            $query = $this->db->query('  SELECT p.link_qrcode,
                                                p.link_qrcodeurl,
                                                p.payment_id,
                                                o.order_status_id
                                            FROM oc_order o
                                        LEFT JOIN oc_payment_pix p
                                            ON (o.order_id = p.order_id)
                                            WHERE o.order_id =' . $this->session->data['payment_session']['order_id'])->row;

            if (isset($query['link_qrcode'])) {
                $this->data['link_qrcode'] = $query['link_qrcode'];
            }
            if (isset($query['link_qrcodeurl'])) {
                $this->data['link_qrcodeurl'] = "data:image/png;base64," . $query['link_qrcodeurl'];
            }
            if (isset($query['payment_id'])) {
                $this->data['payment_id'] = $query['payment_id'];
            }

            $this->data['reloadPayment'] = false;

            $this->data['link_order'] = $this->url->link('account/order/info', 'order_id=' . $this->session->data['payment_session']['order_id']);

            break;
        }

        $this->load->dmodel('checkout/order');
        $status_info = $this->dmodel_checkout_order->getOrderStatus($query['order_status_id']);
        $this->data['status_descricao'] = $status_info['descricao'];
        if ($status_info['order_status_id'] == $this->config->get('pagarme_order_status_canceled')) {
            $this->session->data['orderPaymentCanceled'] = 'Pedido cancelado';
        }

        $this->template = $this->config->get('config_template') . '/template/success/' . $this->session->data['payment_session']['code_type'] . '.twig';
        $this->response->setOutput($this->render());

        } else {
        return false;
        }
    }

    public function validate()
    {
      $this->load->dmodel('checkout/order');

      $error = array();

      $data_atual = new DateTime($this->db->dataAtual(), new DateTimeZone('America/Sao_Paulo'));
      $data_atual = $data_atual->format("Y-m-d");

      if (!$this->debug) {
        $this->language->load('payment/captcha');

        $google_captcha = $this->config->get('google_recaptcha_site_key') && $this->config->get('google_recaptcha_secret_key') && $this->config->get('wg24_google_recaptcha_status');
        $valid_captcha = $this->ftrgeralregistry->validateCaptcha($google_captcha ? $this->request->post['g-recaptcha-response'] : $this->request->post['captcha']);

        if (!$valid_captcha){
          $google_captcha ? $error['g-recaptcha'] = $this->language->get('t_e_google_captcha') : $error['captcha'] = $this->language->get('t_e_captcha');
        }
      }

        $lock = new limiterlock($this->registry);
        $customer_lock = $lock->checklimit( $this->customer);

        if($customer_lock){
            $error['t_e_limiter_ip'] = $lock->getInformation();
        }

        // Recaptcha Google, apos validação em varios locais matar sessao para que na proxima faça a validação.
        if(isset($this->session->data['gcapcha'])){
            unset($this->session->data['gcapcha']);
        }

      if (count($error)>0) {
        return $error;
      } else {
        return false;
      }

    }

    public function getQRCodeStatus($qrCodeId = null, $orderId = null) 
    {
        $logData = [
            'tag'      => '[' . get_class($this) . ']',
            'event'    => 'cielo_api_pix_error',
            'datetime' => date('Y-m-d H:i:s'),
        ];
        $this->response->addHeader('Content-Type: application/json');

        if ($this->request->server['REQUEST_METHOD'] == 'GET' && $qrCodeId) {
            $qrcodeid = $qrCodeId;
            $response = [];

            if ($qrcodeid && $this->cieloapi) {
                $qryOk = true;
                $queryStatus = $this->model_payment_cielo_api_pix->getLastQueryPixStatus($qrcodeid);

                if ($queryStatus && isset($queryStatus['data_criacao']) && 
                   (!isset($queryStatus['status_pagamento']) || $queryStatus['status_pagamento'] == 1)) {
                    $pixpdv_dh_consulta = new DateTime($queryStatus['pixpdv_dh_consulta']);
                    $data_criacao = new DateTime($queryStatus['data_criacao']);
                    $agora = new DateTime();
                    $diferenca_cricao = $pixpdv_dh_consulta->getTimestamp() - $data_criacao->getTimestamp();
                    $diferenca_atual = $agora->getTimestamp() - $pixpdv_dh_consulta->getTimestamp();
                    if ($diferenca_cricao > 0) {
                        if ($diferenca_cricao < 10 && $diferenca_atual < 10) {
                            $qryOk = false;
                        }
                    }
                    $logData['diferenca_criacao'] = $diferenca_cricao;
                    $logData['diferenca_atual'] = $diferenca_atual;
                }
                if ($qryOk || $queryStatus['pixpdv_txid'] == null) {
                    $statusResponse = $this->cieloapi->getPagamentoPix($qrcodeid);

                    if (isset($statusResponse->Payment->Status)) {
                        try {
                            $ignoreStatus = true;
                            if ($queryStatus['pixpdv_txid'] == null) {
                                $ignoreStatus = false;
                            }
                            $logData['message'] = 'Processando status do QR Code ' . $qrCodeId . 
                                                  ': ' . $statusResponse->Payment->Status .
                                                  '. Pedido: ' . ($orderId ?? 'desconhecido') .
                                                  '. TxId: ' . (($statusResponse->Payment->PaymentId ?? $statusResponse->Payment->AcquirerOrderId) ?? 'nulo');
                            $this->processPaymentPix($ignoreStatus, $orderId, $statusResponse->Payment->Status, $qrCodeId, ($statusResponse->Payment->PaymentId ?? $statusResponse->Payment->AcquirerOrderId));
                        } catch (\Exception $e) {
                            $logData = [
                                'message'  => 'Erro no processamento do status do QR Code ' . $qrCodeId . ': ' . $e->getMessage(),
                                'exception'=> [
                                    'type'    => get_class($e),
                                    'message' => $e->getMessage(),
                                    'code'    => $e->getCode(),
                                    'file'    => $e->getFile(),
                                    'line'    => $e->getLine(),
                                    'trace'   => $e->getTraceAsString()
                                ],
                            ];
                            $response['error'] = 'Erro interno ao processar o status do pagamento.';
                        }
                    } else {
                        $logData['message'] = 'Erro: A API Cielo não retornou o status para o QR Code ' . $qrCodeId . '. Resposta: ' . json_encode($statusResponse);
                    }
                    $response = $statusResponse;
                } else if (!$qryOk) {
                    $logData['message'] = 'Consulta ignorada para o QR Code ' . $qrCodeId . ' devido à última consulta recente.';
                }
            } else {
                $this->response->addHeader('HTTP/1.1 400 Bad Request');
                $logData['message'] = 'Parâmetro qrcodeid é obrigatório ou a classe CIELO PIX não foi inicializada.';
                $response['error'] = 'Parâmetro qrcodeid é obrigatório ou a classe CIELO PIX não foi inicializada.';
            }

            if ($this->config->get('config_cielo_api_pix_log') && !empty($logData['message'])) {
                $this->log->write(json_encode($logData, JSON_UNESCAPED_UNICODE));
            }
            return $response;
        } else {
            if ($this->config->get('config_cielo_api_pix_log')) {
                $logData['message'] = 'Chamada inválida para getQRCodeStatus. Método: ' . $this->request->server['REQUEST_METHOD'] . ', qrcodeid: ' . ($qrCodeId ?? 'nulo');
                $this->log->write(json_encode($logData, JSON_UNESCAPED_UNICODE));
                return ['error' => 'Método inválido ou parâmetro qrcodeid ausente.'];
            }
        }
    }

    private function processPaymentPix($ignore = true, $orderId = null, $statusPayment = null, $qrCodeId = null, $txId = null): void
    {
        switch (mb_strtoupper($statusPayment)) {
            case mb_strtoupper(Type_CieloApiPixStatus::Tp_PaymentConfirmed):
                $newStatus = $this->config->get('cielo_api_pix_order_status_approved');
                $ignore = false;
                break;
            case mb_strtoupper(Type_CieloApiPixStatus::Tp_Aborted):
            case mb_strtoupper(Type_CieloApiPixStatus::Tp_NotFinished):
            case mb_strtoupper(Type_CieloApiPixStatus::Tp_Refunded):
                $newStatus = $this->config->get('cielo_api_pix_order_status_canceled');
                $ignore = false;
                break;
            default:
                $newStatus = $this->config->get('cielo_api_pix_order_status_open');
                break;
        }

        if ($ignore) {
            return;
        }

        if (!$qrCodeId) {
            throw new Exception("Erro retorno callback Cielo Pix. Sem retorno de dados.");
        }

        if (!$statusPayment) {
            throw new Exception("Erro retorno callback Cielo Pix. Sem retorno de status.");
        }

        try {
            $this->load->model('payment/cielo_api_pix');
            $this->load->dmodel('payment/payment_info');
            $this->load->dmodel('checkout/order');

            $orderId = $this->model_payment_cielo_api_pix->getOrderIdByIntegracao($qrCodeId);

            if (!$orderId) {
                throw new Exception("Erro retorno callback Cielo Pix. Pedido não encontrado para o código de integração: " . $qrCodeId);
            }

            $this->dmodel_checkout_order->update($orderId, $newStatus, "", true);
            
            $dhAtual = new DateTime();
            $paymentInfo = [];
            $paymentInfo['order_id'] = $orderId;
            $paymentInfo['status_pagamento'] = $newStatus;
            $paymentInfo['pixpdv_txid'] = $txId ?? null;
            $paymentInfo['pixpdv_dh_consulta'] = $dhAtual->format('Y-m-d H:i:s');
            $this->load->dmodel('payment/payment_info');
            try {
                $this->dmodel_payment_payment_info->updatePaymentPix($paymentInfo);
            } catch (\Throwable $e) {
                $this->log->write('Erro em processPaymentPix: ' . $e->getMessage());
            }
            

        } catch (Exception $e) {
             $this->log->write('Erro retorno callback CIELO PIX. Erro: ' . $e->getMessage() . '. Pedido ' . ($orderId ?? 'desconhecido'));
             throw new \Exception('Erro retorno callback CIELO PIX. Erro: ' . $e->getMessage() . '. Pedido ' . ($orderId ?? 'desconhecido'));
        }
    }


    public function reloadPix(){
        $this->load->model('payment/cielo_api_pix');

        if(isset($this->request->get['id']) AND $this->request->get['id'] != ''){
            $this->session->data['payment_session']['code'] = 'cielo_api_pix';
            $this->session->data['payment_session']['order_id'] = $this->request->get['id'];
        }

        try{
            $this->session->data['order_id'] = $this->session->data['payment_session']['order_id'];

            $this->load->model('account/order');
            $order_info = $this->model_account_order->getOrder($this->session->data['order_id']);

            $this->load->dmodel('checkout/order');
            $order_info2 = $this->dmodel_checkout_order->getOrder($this->session->data['order_id']);

            $order_info = array_merge($order_info, $order_info2);

            $valor = $order_info['total'];

            $vvalorPedido = number_format($valor, 2, '.', '');//Cielo requisita o valor em centavos
            $compratotal = number_format($vvalorPedido, 2, '', '');

            $tipoDoc = empty($order_info['cnpj']) ? 'CPF' : 'CNPJ';
            $document = $tipoDoc == 'CPF' ? preg_replace('/\D/', '', $order_info['cpf']) : preg_replace('/\D/', '', $order_info['cnpj']);

            $this->dadosTransacao = array(
                'id_pedido' => $this->session->data['order_id'],
                'valor' => $compratotal
            );

            $this->dadosTransacao['custommer'] = array(
                'nome_cliente' => $order_info['payment_firstname'] . " " . $order_info['payment_lastname'],
                'email_cliente' => $order_info['email'],
                'documento' => $document,
                'tipo_documento' => $tipoDoc
            );

            $resultPix = $this->cieloapi->cieloPagamentoPix($this->dadosTransacao);

            if (!$resultPix || !isset($resultPix->Payment)) {
                throw new Exception("Erro ao gerar o QR Code. Resposta da API Cielo: " . json_encode($resultPix));
            }

            $result = (object) [
                'link_qrcode' => "data:image/png;base64," . $resultPix->Payment->QrCodeBase64Image,
                'status' => $resultPix->Payment->Status,
                'payment_id' => $resultPix->Payment->PaymentId,
                'link_qrcodeurl' => "data:image/png;base64," . $resultPix->Payment->QrCodeBase64Image,
                'additional_data' => [
                    'qr_codeImage' => "data:image/png;base64," . $resultPix->Payment->QrCodeBase64Image,
                    'qr_code' => $resultPix->Payment->QrCodeString,
                    'creation_date_qrcode' => date('Y-m-d H:i:s', strtotime($resultPix->Payment->ReceivedDate ?? date('Y-m-d H:i:s'))),
                    'expiration_date_qrcode' => date('Y-m-d H:i:s', strtotime("+" . ($resultPix->Payment->QrCode->Expiration ?? 8600) . " seconds"))
                ]
            ];

            $this->model_payment_cielo_api_pix->setPaymentPix($order_info['order_id'], $resultPix);
            
            $this->load->controller('account/order');
            $controllerAccountOrder = new ControllerAccountOrder($this->registry);
            $result->payment_pix = $controllerAccountOrder->getPaymentPix($order_info);
            $this->response->setOutputjson($result);
        }catch (Exception $e){
            $this->response->setOutputjson($e);
        }
    }

    public function getOrder()
    {
        $this->load->dmodel('checkout/order');

        $order_info = $this->dmodel_checkout_order->getOrder($this->session->data['order_id']);

        $this->dmodel_checkout_order->confirm($order_info['order_id'], $this->config->get('cielo_api_pix_order_status_open'), '');

        return $order_info;
    }

}
