<?php
/**
 * 2021 CAWL Online Payments
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License 3.0 (AFL-3.0).
 * It is also available through the world-wide-web at this URL: https://opensource.org/licenses/AFL-3.0
 *
 * @author    PrestaShop partner
 * @copyright 2021 CAWL Online Payments
 * @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
 */
if (!defined('_PS_VERSION_')) {
    exit;
}
require_once 'vendor/autoload.php';

use Monolog\Logger;
use PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer;
use WorldlineOP\PrestaShop\Utils\Tools as ToolsWorldline;

/**
 * Class Cawlop
 */
class Cawlop extends PaymentModule
{
    /** @var string */
    public $theme;

    /** @var ServiceContainer */
    private $serviceContainer;

    /** @var Logger */
    public $logger;

    /**
     * Cawlop constructor.
     */
    public function __construct()
    {
        $this->name = 'cawlop';
        $this->author = 'Cawl Online Payments';
        $this->version = '2.0.33';
        $this->tab = 'payments_gateways';
        // $this->module_key = '089d13d0218de8085259e542483f4438'; TODO: UPDATE MODULE KEY WHEN MODULE IS RELEASING
        $this->currencies = true;
        $this->currencies_mode = 'checkbox';
        parent::__construct();
        $this->bootstrap = true;
        $this->ps_versions_compliancy = ['min' => '8', 'max' => '9.9.99'];
        // @formatter:off
        $this->displayName = $this->l('CAWL e-commerce');
        $this->description = $this->l('This module offers a 1-click integration to start accepting payments with the Cawl e-commerce APIs. Grow your revenues by offering your customers with global and regional payment methods to sell across Europe.');
        // @formatter:on
        $this->theme = Tools::version_compare(_PS_VERSION_, '1.7.7', '>=') ? 'new-theme' : 'legacy';
    }

    /**
     * @return bool
     */
    public function install()
    {
        /** @var WorldlineOP\PrestaShop\Installer\Installer $installer */
        $installer = $this->getService('cawlop.installer');
        if (false === parent::install()) {
            $installer->getLogger()->error('parent::install() returns false');

            return false;
        }
        try {
            $installer->run();
        } catch (\WorldlineOP\PrestaShop\Exception\ExceptionList $list) {
            /** @var \Exception $item */
            foreach ($list as $item) {
                $installer->getLogger()->error(sprintf('%s - File: %s - Line: %s - Trace: %s', $item->getMessage(), $item->getFile(), $item->getLine(), $item->getTraceAsString()));

                return false;
            }
        } catch (Exception $e) {
            $installer->getLogger()->error(sprintf('%s - File: %s - Line: %s - Trace: %s', $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString()));
            $this->_errors[] = $this->l('Cawl module could not be installed. Please check the logs inside the module "logs" directory.');

            return false;
        }

        return true;
    }

    /**
     * @return bool
     */
    public function uninstall()
    {
        if (parent::uninstall()) {
            Configuration::deleteByName('CAWLOP_ACCOUNT_SETTINGS');
            Configuration::deleteByName('CAWLOP_ADVANCED_SETTINGS');
            Configuration::deleteByName('CAWLOP_PAYMENT_METHODS_SETTINGS');
            Configuration::deleteByName('CAWLOP_SHOW_ADVANCED_SETTINGS');
            ToolsWorldline::removeSymfonyCache();
            return true;
        }

        return false;
    }

    public function disable($force_all = false)
    {
        if (parent::disable($force_all)) {
            ToolsWorldline::removeSymfonyCache();
            return true;
        }
        return false;
    }

    /**
     * @return Logger
     */
    public function getLogger()
    {
        static $logger;

        if (null === $logger) {
            /** @var Logger $logger */
            $logger = $this->getService('cawlop.logger');
        }

        return $logger;
    }

    /**
     * @param string $serviceName
     *
     * @return mixed
     */
    public function getService($serviceName)
    {
        if ($this->serviceContainer === null) {
            $this->serviceContainer = new \PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer(
                $this->name . str_replace('.', '', $this->version),
                $this->getLocalPath()
            );
        }

        return $this->serviceContainer->getService($serviceName);
    }

    /**
     * @throws PrestaShopException
     */
    public function getContent()
    {
        $mboInstaller = new \Prestashop\ModuleLibMboInstaller\DependencyBuilder($this);
        if (!$mboInstaller->areDependenciesMet())
        {
            $dependencies = $mboInstaller->handleDependencies();
            $this->smarty->assign('dependencies', $dependencies);
            return $this->display(__FILE__, 'views/templates/admin/dependency_builder.tpl');
        }

        Tools::redirectAdmin($this->context->link->getAdminLink('AdminCawlopConfiguration'));
    }

    /**
     * @return Context
     */
    public function getModuleContext()
    {
        return $this->context;
    }

    public function hookActionFrontControllerSetMedia()
    {
        $controller = Tools::getValue('controller');

        switch ($controller) {
            case 'order':
                $this->context->controller->registerJavascript(
                    'worldineoc-js-sdk',
                    'https://payment.cawl-solutions.fr/hostedtokenization/js/client/tokenizer.min.js',
                    ['server' => 'remote', 'priority' => 1, 'position' => 'head', 'attribute' => 'defer']
                );
                $this->context->controller->registerStylesheet(
                    'worldlineop-css-paymentOptions',
                    $this->getPathUri() . 'views/css/front.css?version=' . $this->version,
                    ['server' => 'remote']
                );
                $this->context->controller->registerJavascript(
                    'worldlineop-js-paymentOptions',
                    $this->getPathUri() . 'views/js/paymentOptions.js?version=' . $this->version,
                    ['position' => 'head', 'priority' => 1000, 'server' => 'remote']
                );
                break;
            case 'redirect':
                $this->context->controller->registerJavascript(
                    'worldlineop-redirect-javascript',
                    $this->getPathUri() . 'views/js/redirect.js',
                    ['position' => 'bottom', 'priority' => 1000, 'server' => 'remote']
                );
                break;
        }
    }

    /**
     * @throws PrestaShopException
     */
    public function hookActionAdminControllerSetMedia()
    {
        if (Tools::getValue('controller') == 'AdminOrders') {
            Media::addJsDef([
                'worldlineopAjaxTransactionUrl' => $this->context->link->getAdminLink(
                    'AdminCawlopAjaxTransaction',
                    true,
                    [],
                    ['ajax' => 1, 'token' => Tools::getAdminTokenLite('AdminCawlopAjaxTransaction')]
                ),
                'worldlineopGenericErrorMessage' => $this->l('An error occurred while processing your request. Please try again.'),
                'alertRefund' => $this->l('Do you confirm the refund of the funds?'),
                'alertCapture' => $this->l('Do you confirm the capture of the transaction?'),
                'alertCancel' => $this->l('Do you confirm the cancellation of the transaction?'),
            ]);
            $this->context->controller->addCSS($this->getPathUri() . 'views/css/admin_order.css');
        }
    }

    /**
     * @return string
     */
    public function hookDisplayBackOfficeFooter()
    {
        if (Tools::getValue('controller') == 'AdminCawlopConfiguration') {
            $this->context->smarty->assign('cawlopModulePathUri', $this->getPathUri());

            return $this->display(dirname(__FILE__), 'views/templates/admin/hookDisplayBackOfficeFooter.tpl');
        }

        return '';
    }

    /**
     * @param array $params
     *
     * @return \PrestaShop\PrestaShop\Core\Payment\PaymentOption[]
     */
    public function hookPaymentOptions($params)
    {
        if (!$this->active) {
            return [];
        }

        try {
            /** @var \WorldlineOP\PrestaShop\Presenter\PaymentOptionsPresenter $paymentOptionsPresenter */
            $paymentOptionsPresenter = $this->getService('cawlop.payment.presenter');
        } catch (Exception $e) {
            $this->logger->error('Error while presenting payment options', ['message' => $e->getMessage()]);

            return [];
        }

        /** @var \PrestaShop\PrestaShop\Core\Payment\PaymentOption[] $options */
        $options = $paymentOptionsPresenter->present();

        return $options;
    }

    /**
     * @return string
     */
    public function hookDisplayPaymentByBinaries()
    {
        return $this->context->smarty->fetch($this->getLocalPath() . '/views/templates/front/hookDisplayPaymentByBinaries.tpl');
    }

    /**
     * @param $params
     *
     * @return string
     */
    public function hookDisplayPaymentTop($params)
    {
        if (Tools::getValue('worldlineopDisplayPaymentTopMessage')) {
            return $this->context->smarty->fetch($this->getLocalPath() . '/views/templates/front/hookDisplayPaymentTop.tpl');
        }

        return '';
    }

    /**
     * @param array $params
     *
     * @return string
     */
    public function hookDisplayCustomerAccount($params)
    {
        return $this->display(dirname(__FILE__), 'views/templates/front/hookCustomerAccount.tpl');
    }

    /**
     * @param int $idOrder
     *
     * @return string
     *
     * @throws Exception
     */
    public function hookAdminOrderCommon($idOrder)
    {
        $order = new Order((int) $idOrder);
        if (!Validate::isLoadedObject($order)) {
            throw new Exception('Cannot load order');
        }

        if ($order->id_shop != $this->context->shop->id || Shop::getContext() !== Shop::CONTEXT_SHOP) {
            $this->context->smarty->assign([
                'worldlineopError' => sprintf($this->l('Please change shop context to shop ID %d'), $order->id_shop),
                'worldlineopTheme' => '',
            ]);

            return $this->display(dirname(__FILE__), 'views/templates/admin/hookAdminOrder_container.tpl');
        }
        try {
            /** @var \WorldlineOP\PrestaShop\Presenter\TransactionPresenter $transactionPresenter */
            $transactionPresenter = $this->getService('cawlop.transaction.presenter');
            /** @var \WorldlineOP\PrestaShop\Presenter\ModuleConfigurationPresenter $settingsPresenter */
            $settingsPresenter = $this->getService('cawlop.settings.presenter');

            $this->context->smarty->assign([
                'worldlineopError' => '',
                'worldlineopTheme' => $this->theme,
                'transactionsData' => $transactionPresenter->present($idOrder),
                'settingsData' => $settingsPresenter->present(),
            ]);
        } catch (Exception $e) {
            $this->context->smarty->assign([
                'worldlineopError' => $e->getMessage(),
                'worldlineopTheme' => '',
            ]);
        }

        return $this->display(dirname(__FILE__), 'views/templates/admin/hookAdminOrder_container.tpl');
    }

    /**
     * @param array $params
     *
     * @return string
     */
    public function hookDisplayAdminOrderMainBottom($params)
    {
        if (Tools::version_compare(_PS_VERSION_, '1.7.7', '<')) {
            return '';
        }

        try {
            return $this->hookAdminOrderCommon(Tools::getValue('id_order'));
        } catch (Exception $e) {
            return '';
        }
    }

    /**
     * @param array $params
     *
     * @return string
     */
    public function hookDisplayAdminOrderLeft($params)
    {
        if (Tools::version_compare(_PS_VERSION_, '1.7.7', '>=')) {
            return '';
        }

        try {
            return $this->hookAdminOrderCommon(Tools::getValue('id_order'));
        } catch (Exception $e) {
            return '';
        }
    }

    /**
     * @param array $params
     *
     * @return string
     *
     * @throws PrestaShopException
     */
    public function hookDisplayPDFInvoice($params)
    {
        /** @var OrderInvoice $invoice */
        $invoice = $params['object'];
        $order = new Order((int) $invoice->id_order);
        if (!Validate::isLoadedObject($order)) {
            return '';
        }
        /** @var \WorldlineOP\PrestaShop\Repository\TransactionRepository $transactionRepository */
        $transactionRepository = $this->getService('cawlop.repository.transaction');
        /** @var WorldlineopTransaction|false $transaction */
        $transaction = $transactionRepository->findByIdOrder($order->id);
        if (false === $transaction) {
            return '';
        }
        $transactionId = substr($transaction->reference, 0, -3);
        if ('' === $transactionId) {
            $transactionId = $transaction->reference;
        }
        $this->context->smarty->assign([
            'worldlineop_transaction_id' => $transactionId,
        ]);

        return $this->display(dirname(__FILE__), 'views/templates/admin/hookDisplayPDFInvoice.tpl');
    }

    /**
     * @param mixed[] $params
     *
     * @return string
     */
    public function hookDisplayAdminProductsExtra($params)
    {
        $idProduct = (int) $params['id_product'];

        // PS 8.1+: editable field lives in the dedicated CAWL tab (hookActionProductFormBuilderModifier).
        // Show read-only summary here.
        if (Tools::version_compare(_PS_VERSION_, '8.1.0', '>=')) {
            $twig = method_exists($this, 'getTwig') ? $this->getTwig() : $this->get('twig');
            if (!$twig) {
                return '';
            }

            $typeLabels = [
                \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_NONE => $this->l('None'),
                \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_FOOD_DRINK => $this->l('Food & Drink'),
                \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_HOME_GARDEN => $this->l('Home & Garden'),
                \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_GIFT_FLOWERS => $this->l('Gift & Flowers'),
            ];
            $currentType = $idProduct ? ToolsWorldline::getGiftCardTypeByIdProduct($idProduct) : \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_NONE;

            return $twig->render('@Modules/cawlop/views/templates/admin/product_extra_module.html.twig', [
                'currentTypeLabel' => html_entity_decode($typeLabels[$currentType] ?? $this->l('None'), ENT_QUOTES, 'UTF-8'),
            ]);
        }

        // PS 8.0 with legacy product page: render editable Smarty form directly.
        $this->context->smarty->assign([
            'worldlineopGCTypeNone' => \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_NONE,
            'worldlineopGCTypeFoodDrink' => \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_FOOD_DRINK,
            'worldlineopGCTypeHomeGarden' => \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_HOME_GARDEN,
            'worldlineopGCTypeGiftFlowers' => \WorldlineOP\PrestaShop\Builder\HostedPaymentRequestBuilder::GIFT_CARD_PRODUCT_TYPE_GIFT_FLOWERS,
            'worldlineopGCSelectedType' => \WorldlineOP\PrestaShop\Utils\Tools::getGiftCardTypeByIdProduct($idProduct),
        ]);

        return $this->display(dirname(__FILE__), 'views/templates/admin/hookDisplayAdminProductsExtra.tpl');
    }

    /**
     * PS 8.1+: adds the Cawl gift card tab to the product form.
     *
     * @param mixed[] $params
     */
    public function hookActionProductFormBuilderModifier(array $params): void
    {
        if (!Tools::version_compare(_PS_VERSION_, '8.1.0', '>=')) {
            return;
        }

        /** @var \WorldlineOP\PrestaShop\Form\Modifier\ProductFormModifier|null $modifier */
        $modifier = $this->get(\WorldlineOP\PrestaShop\Form\Modifier\ProductFormModifier::class);
        if ($modifier === null) {
            return;
        }

        $productId = isset($params['id']) ? (int) $params['id'] : null;
        $modifier->modify($productId, $params['form_builder']);
    }

    /**
     * PS 8.1+: saves gift card type from the CAWL tab after the product form is submitted.
     *
     * @param mixed[] $params
     */
    public function hookActionAfterUpdateProductFormHandler(array $params): void
    {
        if (!isset($params['id'])) {
            return;
        }

        // GiftCardTabType is mapped=>false so its data is not in $params['form_data'].
        // Read directly from POST: product[worldlineop_gift_card][gift_card_type].
        $productPost = Tools::getValue('product');
        $giftCardType = is_array($productPost)
            ? ($productPost['worldlineop_gift_card']['gift_card_type'] ?? null)
            : null;

        if ($giftCardType === null) {
            return;
        }

        try {
            Db::getInstance()->insert(
                'cawlop_product_gift_card',
                [
                    'id_product' => (int) $params['id'],
                    'product_type' => pSQL($giftCardType),
                ],
                false, true, Db::ON_DUPLICATE_KEY
            );
        } catch (Exception $e) {
            $this->logger->error($e->getMessage(), ['trace' => $e->getTraceAsString()]);
        }
    }

    /**
     * @param mixed[] $params
     *
     * @return void
     */
    public function hookActionProductUpdate($params)
    {
        if ($form = Tools::getValue('worldlineopGiftCard')) {
            $idProduct = $params['id_product'];
            try {
                Db::getInstance()->insert('cawlop_product_gift_card', ['id_product' => (int) $idProduct, 'product_type' => pSQL($form['type'])], false, true, Db::ON_DUPLICATE_KEY);
            } catch (Exception $e) {
                $this->logger->error($e->getMessage(), ['trace' => $e->getTraceAsString()]);
            }
        }
    }
}
