<?php

/*
 ************************************************************************
 Copyright [2013] [PagSeguro Internet Ltda.]

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 ************************************************************************
 */

/**
 * Class Controller Payment PagSeguro Notification
 * Class responsible for receiving and handling of notifications sent by PagSeguro.
 */
include_once 'PagSeguroLibrary/PagSeguroLibrary.php';
class ControllerPaymentPagSeguroNotification extends Controller
{

	/**
	 * $_POST['notificationType']
	 * @var string
	 */
	private $notification_type;

	/**
	 * $_POST['notificationCode']
	 * @var string
	 */
	private $notification_code;

	/**
	 * Reference purchase
	 * @var int
	 */
	private $reference;

	/**
	 * Reference purchase
	 * @var int
	 */
	private $code;

	/**
	 * Code language active in the system
	 * @var string
	 */
	private $code_language;

	/**
	 * Status PagSeguro
	 * @var array
	 */
	private $array_order_status;

	/**
	 *Tipos De Pagamentos
	 *$var array
	 */
	private $array_paymentMethod;

	/**
	 * PagSeguroAccountCredentials
	 * @var PagSeguroAccountCredentials
	 */
	private $obj_credentials;

	/**
	 * PagSeguroNotificationType
	 * @var PagSeguroNotificationType
	 */
	private $obj_notification_type;

	/**
	 * PagSeguroNotificationService
	 * @var Transaction
	 */
	private $obj_transaction;

	private $log;

	/**
	 * Wrong special characters
	 * @var array
	 */
	public $caracteresCorrompidos = [

		// UTF-8 interpretado como ISO-8859-1 / Windows-1252
		'Ã€','Ã�','Ã‚','Ãƒ','Ã„','Ã…',
		'Ã†','Ã‡','Ãˆ','Ã‰','ÃŠ','Ã‹',
		'ÃŒ','Ã�','ÃŽ','Ã�','Ã�','Ã‘',
		'Ã’','Ã“','Ã”','Ã•','Ã–','Ã—',
		'Ã˜','Ã™','Ãš','Ã›','Ãœ','Ã�',
		'Ãž','ÃŸ',

		'Ã ','Ã¡','Ã¢','Ã£','Ã¤','Ã¥',
		'Ã¦','Ã§','Ã¨','Ã©','Ãª','Ã«',
		'Ã¬','Ã­','Ã®','Ã¯','Ã°','Ã±',
		'Ã²','Ã³','Ã´','Ãµ','Ã¶','Ã·',
		'Ã¸','Ã¹','Ãº','Ã»','Ã¼','Ã½',
		'Ã¾','Ã¿',

		// Aspas e símbolos tipográficos
		'â€˜','â€™','â€œ','â€�',
		'â€š','â€ž','â€¢','â€¦',
		'â€“','â€”','â€',
		'â„¢','â€¡','â€ ',
		'â‚¬',

		// Outros comuns
		'Â','Â ',
		'Â¡','Â¢','Â£','Â¤','Â¥',
		'Â¦','Â§','Â¨','Â©','Âª',
		'Â«','Â¬','Â®','Â¯',
		'Â°','Â±','Â²','Â³',
		'Â´','Âµ','Â¶','Â·',
		'Â¸','Â¹','Âº','Â»',
		'Â¼','Â½','Â¾','Â¿',

		// Sequências parcialmente quebradas
		'Ð','ð',
		'Ý','ý',
		'þ','Þ',

		// Caracteres Unicode inválidos/substituição
		'�',

		// Sequências frequentes em JSON/XML/API
		'\\u00c3',
		'\\u00a9',
		'\\u00e7',
		'\\u00e3',
		'\\u00f5',

		// Combinações muito comuns PT-BR
		'Ã¡','Ã¢','Ã£','Ã§',
		'Ã©','Ãª','Ã­','Ã³',
		'Ã´','Ãµ','Ãº',

		// Sequências comuns EN
		'â€™',
		'â€œ',
		'â€',
		'â€“',
		'â€”'
	];

	/**
	 * The first method to be called by the notification PagSeguro treatment.
	 */
	public function index()
	{
		$this->_load();

		if (!empty($this->request->post)) {
			$json = json_encode($this->request->post);
		} else {
			$json = file_get_contents('php://input');
		}

		if (!$json || empty($json)) {
			return;
		}
		
		if (!$this->checkAuthenticity($json)) {
			if (!$this->checkAuthenticity($this->normalizarTexto($json))) { //Tentar removendo caracteres especiais
				$request = ["body" => $json, "headers" => getallheaders()];
				$this->log->write("Notificação Pagseguro inválida: " . json_encode($request));
				return;
			}
		}
		
		$notificationData = json_decode($json);

		if ($this->config->get('pagseguro_log')) {
			$this->pagseguro->setLog(['notification' => $notificationData]);
		}

		if (isset($notificationData->charges)) {
			$this->updateCheckoutPayment($notificationData);
		} else if (isset($notificationData->reference_id)) {
			$this->updateCheckout($notificationData);
		}		
	}


	/**
	 * Convert wrong string with special chars
	 * @param string $texto
	 */
	public function normalizarTexto(string $texto) {
		$regex = '/' . implode(
			'|',
			array_map('preg_quote', $this->caracteresCorrompidos)
		) . '/u';

		if (preg_match($regex, $texto)) {
			return  mb_convert_encoding($texto, 'ISO-8859-1', 'UTF-8');
		}
		
		return $texto;
	}


	/**
	 * Check the request authenticity
	 * @param string $json
	 */
	public function checkAuthenticity($json)
	{
		$headers = getallheaders();
		$token = $this->pagseguro->decryptData($this->config->get('pagseguro_token'));
		$signature = hash('sha256', $token . '-' . $json);
		if (!isset($headers['X-Authenticity-Token']) || empty($headers['X-Authenticity-Token']) || $headers['X-Authenticity-Token'] != $signature) {
			return false;
		}
		
		return true;
	}

	/**
	 * Load Model PagSeguro.
	 */
	private function _load()
	{
		$this->load->type('tipopagseguro')	;
    	$this->load->type('tipocomum')	;
		$this->load->model('payment/pagseguro');
		$this->load->dmodel('checkout/order');
		$this->log = $this->registry->get('log');
	}

	/**
	 * Pedido foi pago ou teve alteração de status no pagamento.
	 * @param object $checkoutData
	 */
	private function updateCheckoutPayment($checkoutData)
	{
		$orderId = (int) str_replace('#Pedido_', '', $checkoutData->reference_id);
		if (!$orderId) {
			$this->log->write("Pedido não encontrado para o checkout: " . $checkoutData->reference_id);
			return;
		}
		$this->dmodel_checkout_order->setOrderOtherInfos($orderId, ['pagseguro_checkout' => $checkoutData]);

		$statusRetorno = $checkoutData->charges[0]->status;

		$lastStatus = $this->dmodel_checkout_order->getLastPaymentStatusHistory($orderId);

		if(!$lastStatus || ($statusRetorno <> $lastStatus['status'])) {
			switch($statusRetorno){
				case Type_OrderStatusCheckoutPagseguro::Tp_Waiting :
					$status = $this->config->get('pagseguro_awaiting_order_id');
				break;
				case Type_OrderStatusCheckoutPagseguro::Tp_InAnalysis :
					$status = $this->config->get('pagseguro_processing_order_id');
				break;
				case Type_OrderStatusCheckoutPagseguro::Tp_Paid :
					$status = $this->config->get('pagseguro_processed_order_id');
				break;
				case Type_OrderStatusCheckoutPagseguro::Tp_Declined :
					$status = $this->config->get('pagseguro_declined_order_id');
				break;
				case Type_OrderStatusCheckoutPagseguro::Tp_Canceled :
					$status = $this->config->get('pagseguro_cancelado_order_id');
				break;
				default: 
					$this->log->write("Status não mapeado para Pagseguro: " . $statusRetorno);
					return;
			}

			$this->_updateOrder($status, $orderId);
			$ordem = (isset($lastStatus['ordem']) ? $lastStatus['ordem'] + 1 : 0);
			$this->dmodel_checkout_order->insertPaymentStatusHistory($orderId, $status, $ordem);

			if ($this->config->get('pagseguro_log')) {
				$this->pagseguro->setLog(['updated_order' => $orderId, 'status' => Type_OrderStatusCheckoutPagseguro::getNickFromValue($statusRetorno)]);
			}
		}
	}

	/**
	 * O checkout expirou.
	 * @param object $checkoutData
	 */
	private function updateCheckout($checkoutData)
	{
		$orderId = (int) str_replace('#Pedido_', '', $checkoutData->reference_id);
		if (!$orderId) {
			$this->log->write("Pedido não encontrado para o checkout: " . $checkoutData->reference_id);
			return;
		}
		$this->dmodel_checkout_order->setOrderOtherInfos($orderId, ['pagseguro_checkout' => $checkoutData]);
		
		$statusRetorno = $checkoutData->status;
		$expirationDate = $checkoutData->expiration_date;
		$dataAtual = new DateTime($this->db->dataAtual(), new DateTimeZone('America/Sao_Paulo'));
		$dataExpiracao = new DateTime($expirationDate, new DateTimeZone('America/Sao_Paulo'));	

		$lastStatus = $this->dmodel_checkout_order->getLastPaymentStatusHistory($orderId);

		if((!$lastStatus || $lastStatus['status'] != $this->config->get('pagseguro_processed_order_id')) && ($statusRetorno == "EXPIRED" || ($dataExpiracao >= $dataAtual))){
			$this->dmodel_checkout_order->updateAbandonedOrdersPagseguro($orderId);
	
			$ordem = (isset($lastStatus['ordem']) ? $lastStatus['ordem'] + 1 : 0);
			$this->dmodel_checkout_order->insertPaymentStatusHistory($orderId, $this->config->get('pagseguro_abandoned_order_id'), $ordem);
		}

		if ($this->config->get('pagseguro_log')) {
			$this->pagseguro->setLog(['updated_checkout' => $orderId, 'status' => $statusRetorno]);
		}
	}

	

	/**
	 * Update table order and order_history
	 * @param int $id_order_status
	 */
	private function _updateOrder($id_order_status, $orderId = null)
	{
		if ($orderId === null) {
			$orderId = $this->reference;
		}
		$this->model_payment_pagseguro->saveOrderHistory($orderId, $id_order_status);
		$this->model_payment_pagseguro->updateOrder($orderId, $id_order_status, $this->code);
	}
}
