<?php

/**
 * NOTA SOBRE LA LICENCIA DE USO DEL SOFTWARE
 * 
 * El uso de este software está sujeto a las Condiciones de uso de software que
 * se incluyen en el paquete en el documento "Aviso Legal.pdf". También puede
 * obtener una copia en la siguiente url:
 * http://www.redsys.es/wps/portal/redsys/publica/areadeserviciosweb/descargaDeDocumentacionYEjecutables
 * 
 * Redsys es titular de todos los derechos de propiedad intelectual e industrial
 * del software.
 * 
 * Quedan expresamente prohibidas la reproducción, la distribución y la
 * comunicación pública, incluida su modalidad de puesta a disposición con fines
 * distintos a los descritos en las Condiciones de uso.
 * 
 * Redsys se reserva la posibilidad de ejercer las acciones legales que le
 * correspondan para hacer valer sus derechos frente a cualquier infracción de
 * los derechos de propiedad intelectual y/o industrial.
 * 
 * Redsys Servicios de Procesamiento, S.L., CIF B85955367
 */

namespace Redsys\Redsys\Controller\Checkout;

use Magento\Catalog\Model\ProductRepository;
use Magento\Checkout\Model\Cart;
use Magento\Checkout\Model\Session;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Data\Form\FormKey;
use Magento\Framework\DB\Transaction;
use Magento\Framework\View\Result\PageFactory;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
use Magento\Sales\Model\Service\InvoiceService;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Api\InvoiceRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Quote\Api\Data\CartItemInterfaceFactory;

use Magento\Framework\App\CsrfAwareActionInterface;

use Redsys\Redsys\Controller\RedsysBizumController;
use Redsys\Redsys\Helper\RedsysApi;
use Redsys\Redsys\Helper\RedsysLibrary;
use Redsys\Redsys\Helper\RefundManager;

class NotifyBizum extends \Magento\Framework\App\Action\Action implements \Magento\Framework\App\CsrfAwareActionInterface
{
	protected $_resultPageFactory;
	protected $_redsysController;
	protected $_session;
	protected $_invoiceService;
	protected $_invoiceSender;
	protected $_cart;
	protected $_formKey;
	protected $_productRepository;
	protected $_transaction;
	protected $_cartItemFactory;

	public function __construct(Context $context, Session $session, PageFactory $resultPageFactory, StoreManagerInterface $storeManager, RedsysBizumController $redsysController, InvoiceService $invoiceService, InvoiceSender $invoiceSender, Cart $cart, ProductRepository $productRepository, FormKey $formKey, InvoiceRepositoryInterface $invoiceRepository, Transaction $transaction, CartItemInterfaceFactory $cartItemFactory)
	{
		$this->_session = $session;
		$this->_invoiceSender = $invoiceSender;
		$this->_invoiceService = $invoiceService;
		$this->_redsysController = $redsysController;
		$this->_resultPageFactory = $resultPageFactory;
		$this->_cart = $cart;
		$this->_formKey = $formKey;
		$this->_productRepository = $productRepository;
		$this->_transaction        = $transaction;
		$this->_cartItemFactory = $cartItemFactory;

		parent::__construct($context);
	}


	public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException 
	{
		return null;
	}

	public function validateForCsrf(RequestInterface $request): ?bool
	{
		return true;
	}

	/*
	private function validateRequest(
		HttpRequest $request,
		ActionInterface $action
	) {
		$valid = null;
		if ($action instanceof CsrfAwareActionInterface) {
			$valid = $action->validateForCsrf($request);
		}
		if ($valid === null) {
			$valid = !$request->isPost()
				|| $request->isAjax()
				|| $this->formKeyValidator->validate($request);
		}

		return $valid;
	}
	*/

	public function execute()
	{
		try {
			$resultPage = $this->_resultPageFactory->create();
			$resultPage->getConfig()->getTitle()->append(__("Notificacion") . " - Redsys");
			$resultPage->getLayout()->initMessages();

			/** Identificamos que la petición ha llegado hasta el validador. */
			http_response_code(100);
			/** Se crea el objeto principal de la clase. */
			$miObj = new RedsysApi();

			/** Se recogen los datos de entrada. **/
			$dsSignatureVersion   = $_POST["Ds_SignatureVersion"] ?? $_GET["Ds_SignatureVersion"] ?? false;
			$dsMerchantParameters = $_POST["Ds_MerchantParameters"] ?? $_GET["Ds_MerchantParameters"] ?? false;
			$dsSignature          = $_POST["Ds_Signature"] ?? $_GET["Ds_Signature"] ?? false;

			/** Se comprueba si la URL ha entrado con parámetros. */
			if (!$dsMerchantParameters or !$dsSignature) {

				http_response_code(400);
				die ('La URL de notificación o del retorno de navegación no contiene parámetros válidos, por lo que no se puede redireccionar de nuevo a la tienda. Revisa tu historial de pedidos accediendo a la tienda de nuevo y en caso de duda contacta con el comercio.');
			}

			$logActivo = $this->_redsysController->get_logactivo();

			/** Obtenemos la clave local. **/
			$claveComercio = $this->_redsysController->get_clave256();

			/** Se decodifican los datos enviados y se carga el array de datos **/
			$decodec = $miObj->decodeMerchantParameters($dsMerchantParameters);
			$miObj->createMerchantSignatureNotif($claveComercio, $dsMerchantParameters, $logActivo);

			/** Se inicializan los objetos necesarios para crear los registros de log. **/
			$merchantData = RedsysLibrary::b64url_decode($miObj->getParameter('Ds_MerchantData'));
			$merchantData = json_decode( $merchantData ); 

			$idCart = $merchantData->idCart;
			$pedido = $miObj->getParameter('Ds_Order'); 
			
			$idLog = RedsysLibrary::generateIdLog($logActivo, $idCart);

			/** Se identifica la operacion en el registro. */
			if (!empty($_POST))
				RedsysLibrary::escribirLog("INFO ", $idLog, "***** VALIDACIÓN DE LA NOTIFICACIÓN  ──  PEDIDO " . $miObj->getParameter('Ds_Order') . " *****");
			else
				RedsysLibrary::escribirLog("INFO ", $idLog, "***** RETORNO DE NAVEGACIÓN  ──  PEDIDO " . $miObj->getParameter('Ds_Order') . " *****");

			RedsysLibrary::escribirLog("DEBUG", $idLog, "Parámetros de la notificación : " . $dsMerchantParameters);
			RedsysLibrary::escribirLog("DEBUG", $idLog, "Firma recibida del TPV Virtual: " . $dsSignature);

			/** Comprobacion de la firma y rechazo del procesamiento si no coinciden. */
			if (!$this->validarFirma($miObj, $dsMerchantParameters, $dsSignature, $claveComercio, $dsSignatureVersion, $idLog)) {
				
				http_response_code(403);
				RedsysLibrary::escribirLog("ERROR", $idLog, "Las firmas no coinciden, la notificación se rechazará con error HTTP 403.");
				die ('La petición no puede ser atendida porque las firmas no coinciden.');
			}

			/** Se obtiene cuál es el estado configurado como "estado final" en la configuración del módulo. */
			$estadoFinal = $this->_redsysController->get_estado();

            /** Se crea el objeto order para poder procesar la notificación. */
			$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
			$order = $objectManager->create('\Magento\Sales\Model\Order')->load($idCart);
                        
            /** Control de navegación en caso de que el cliente sea redirigido al validation. */
            /** Se accede sólo si el POST está vacío pero sí que tenemos merchantParameters. */
            if (empty($_POST) and $dsMerchantParameters) {
                                
                RedsysLibrary::escribirLog("INFO ", $idLog, "Cliente redirigido al validador a través del retorno de navegación.");

                /** URL adonde redirigiremos al cliente. */
                $urlRedirect = $this->_redsysController->getURLSuccess();

                /** Se evalúa si se necesita procesar la notificación usando parámetros GET comprobando si getOrderByCartId nos devuelve un pedido. Si lo hiciera, el pedido existe y no hay que validar. */
                if ($order->getStatus() != $estadoFinal) {

                    RedsysLibrary::escribirLog("INFO ", $idLog, "Se van utilizar los datos recibidos vía GET para validar el pedido " . $pedido . " porque notificacion_get es " . $this->_redsysController->get_notificacion_get());

                    /** Si la validación sale mal, fijamos el checkout como la URL a la que redirigir. */
                    if (!$this->confirmarPedido($miObj, $order, $merchantData, $pedido, $estadoFinal, $idLog))
						$urlRedirect = $this->_redsysController->getURLError();

					$order->addStatusHistoryComment(__('[REDSYS] El pedido se ha registrado usando los parámetros incluidos en el retorno de navegación.'), false);
					$order->save();
                }
                
                RedsysLibrary::escribirLog("DEBUG", $idLog, "Redireccionando cliente a: " . $urlRedirect);

				header("Location: " . $urlRedirect);
                http_response_code(308);

                exit();
            }

            /** Evaluamos si el pedido ya está creado, y si es así, registramos que no lo tenemos que tocar. */
            if ($order->getStatus() == $estadoFinal and $this->_redsysController->get_notificacion_get()) {
				
				http_response_code(422);
				RedsysLibrary::escribirLog("ERROR", $idLog, "Se ha recibido una notificación pero la orden ya está creada.");
				
				die("Se ha recibido una notificación pero la orden ya está creada.");
            
			} else {

				/** Ejectuamos la lógica de confirmación del pedido. */
                $this->confirmarPedido($miObj, $order, $merchantData, $pedido, $estadoFinal, $idLog);
                				
                exit();
			}

        } catch (Exception $e) {
            
            http_response_code(500);
            RedsysLibrary::escribirLog("ERROR", "0000000000000000000000000ERROR", "Excepción en la validación: ".$e->getMessage());

            die("Excepcion en la validacion.");
        }

		return $resultPage;
	}

    private function confirmarPedido($miObj, $order, $merchantData, $pedido, $estadoFinal, $idLog = null) {

        /** Se extraen todos los datos de la notificación. **/
        $total            = (int)$miObj->getParameter('Ds_Amount');  
        $idCart           = $merchantData->idCart;
        $codigo           = (int)$miObj->getParameter('Ds_MerchantCode');
        $terminal         = (int)$miObj->getParameter('Ds_Terminal');
        $moneda           = (int)$miObj->getParameter('Ds_Currency');
        $respuesta        = $miObj->getParameter('Ds_Response');
        $authCode         = $miObj->getParameter('Ds_AuthorisationCode');
        $tipoTransaccion  = (int)$miObj->getParameter('Ds_TransactionType');
        $metodo           = (int)$miObj->getParameter('Ds_ProcessedPayMethod');

        $metodoOrder = "N/A";

        if ($respuesta < 101)
            $metodoOrder = "Autorizada " . $authCode;    
        else if ($respuesta >= 101)
            $metodoOrder = "Denegada " . $respuesta;

        /** Se escriben en el registro los datos recibidos. */
        RedsysLibrary::escribirLog("DEBUG", $idLog, "ID del Carrito: " . $idCart);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Codigo Comercio FUC: " . $codigo);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Terminal: " . $terminal);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Moneda: " . $moneda);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Codigo de respuesta del SIS: " . $respuesta);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Método de Pago: " . $metodo);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Información adicional del módulo: " . $merchantData->moduleComent);

        /** Análisis de respuesta del SIS. */
		$respuestaSIS = RedsysLibrary::checkRespuestaSIS($respuesta);
		$errorBackofficeSIS = $respuestaSIS[0];
        
        $authCode = str_replace("+", "", $authCode);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Código de Autorización: " . $authCode);

        /** Advertimos si es una preautorización que el pedido aún no tiene valor contable. */
        if ($tipoTransaccion == 1){
			$order->addStatusHistoryComment( __('[REDSYS] Esta orden se ha validado usando una PREAUTORIZACIÓN. Recuerde que para realizar la confirmación, deberá hacerlo desde el Portal de Administración del TPV Virtual'), false);
			$order->save();
		}

        /** Se valida el pedido cuando la operación es genuina y válida. */    
        if((int)$respuesta < 101){

            /** Añadimos los mensajes de información a la orden. */
			$order->setBaseTotalPaid($order->getBaseTotalDue());
			$order->setTotalPaid($order->getTotalDue());
    		$order->setState('new')->setStatus($estadoFinal);
			$order->save();

			$order->addStatusHistoryComment(__('[REDSYS] ') . $metodoOrder, false);
			$order->addStatusHistoryComment(__('[REDSYS] Respuesta del SIS: ') . $errorBackofficeSIS, false);
			$order->addStatusHistoryComment(__('[REDSYS] El pedido es válido y se ha registrado correctamente. Número de pedido enviado a Redsys: ') . $pedido, false);
			$order->setIsCustomerNotified(true);
			$order->save();

			if($this->_redsysController->get_genfactura()){
				if(!$order->canInvoice()) {
					$order->addStatusHistoryComment(__("Redsys, imposible generar Factura."), false);
					$order->save();
				} else {
					$transaction = new Transaction();
	
					$invoice=$this->_invoiceService->prepareInvoice($order);
					$invoice->register();
					$invoice->setTransactionId($pedido);
					$invoice->pay();
					$invoice->save();
					$transactionSave = $transaction->addObject($invoice)->addObject($invoice->getOrder());
					$transactionSave->save();
	
					if (!@$this->_invoiceSender->send($invoice))
						$order->addStatusHistoryComment(__("Redsys, imposible enviar Factura."), false);
	
					$order->addStatusHistoryComment(__("Redsys ha generado la Factura del pedido"), false)->save();
				}
			}

			if ($order->canCreditmemo())
				RedsysLibrary::escribirLog("DEBUG", $idLog, "La orden permite devolución", null, __METHOD__);
			else
				RedsysLibrary::escribirLog("DEBUG", $idLog, "La orden no permite devolución", null, __METHOD__);
            
            /** Guardamos la reeferencia si en la notificación está incluida. */
            $merchantIdentifier = $miObj->getParameter('Ds_Merchant_Identifier');
            
            if($this->_redsysController->get_ref() == 1 and $merchantIdentifier!=null) {
                
                $idCustomer = $this->_redsysController->get_customerId();
                $cardNumber=$miObj->getParameter('Ds_Card_Number');
                $brand=$miObj->getParameter('Ds_Card_Brand');
                $cardType=$miObj->getParameter('Ds_Card_Type');
                
				$this->_redsysController->get_reference()->saveReference($idCustomer, $merchantIdentifier, $cardNumber, $brand, $cardType);
            }
            
            /** Se guarda el ID para posteriores operaciones sobre la orden. */
            //WC_Redsys_Refund::saveOrderId($idCart, $pedido, $total);

            /** E imprimimos el resultado en el registro. */
            RedsysLibrary::escribirLog("INFO ", $idLog, "El pedido con ID de carrito " . $idCart . " (" . $pedido . "), y número de orden " . $order->getId() . " es válido y se ha registrado correctamente.");
            
            echo "Pedido validado con éxito ── " . $errorBackofficeSIS;
            http_response_code(200);

            return(1);
        
        } else {

            /** Si se ha producido un error, mostramos que ha habido un error. */
			if($this->_redsysController->get_errorpago()){
				$order->cancel()->setState(\Magento\Sales\Model\Order::STATE_CANCELED, 'canceled', __('[REDSYS] El pedido ha finalizado con errores. Número de pedido enviado a Redsys: ') . $pedido, false);
			}else{
				$order->cancel();
			}
			$order->save();

			$order->addStatusHistoryComment( __('[REDSYS] ') . $metodoOrder, false);
			$order->addStatusHistoryComment( __('[REDSYS] Respuesta del SIS: ') . $errorBackofficeSIS, false);
			$order->setIsCustomerNotified(false);
			$order->save();

            RedsysLibrary::escribirLog("ERROR", $idLog, "El pedido con ID de carrito " . $idCart . " (" . $pedido . "), y número de orden " . $order->getId() . " ha finalizado con errores.");
            RedsysLibrary::escribirLog("ERROR", $idLog, $errorBackofficeSIS);
           
            echo "El pedido ha finalizado con errores ── " . $errorBackofficeSIS;
            http_response_code(200);

            return(0);
        }
    }

    private function validarFirma($miObj, $dsMerchantParameters, $dsSignature, $claveComercio, $dsSignatureVersion, $idLog = false) {
        $logActivo = $this->_redsysController->get_logactivo();
        $dsSignatureLOCAL = $miObj->createMerchantSignatureNotif($claveComercio, $dsMerchantParameters, $logActivo);
        
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Firma calculada notificación  : " . $dsSignatureLOCAL);
        RedsysLibrary::escribirLog("DEBUG", $idLog, "Firma calculada usando la clave de encriptación [" . $dsSignatureVersion . "] " . substr($claveComercio, 0, 3) . "*");

        if ($dsSignature === $dsSignatureLOCAL)
            return true;
        else
            return false;
    }
}
