<?php
/**
 * 2007-2021 PrestaShop
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to http://www.prestashop.com for more information.
 *
 * @author    PrestaShop SA <contact@prestashop.com>
 * @copyright 2007-2021 PrestaShop SA
 * @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
 *  International Registered Trademark & Property of PrestaShop SA
 */

namespace Prestashop\Module\AddonPayments\Services;

use addonpayments;
use ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs;
use ComerciaGlobalPayments\AddonPayments\SDK\Request\Enum\ChargeTypeEnum;
use ComerciaGlobalPayments\AddonPayments\SDK\Request\ExpressCheckout;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponseInterface;
use ComerciaGlobalPayments\AddonPayments\SDK\Response\PaymentSolution;
use Configuration;
use Context;
use Exception;
use ImageManager;
use Media;
use Prestashop\Module\AddonPayments\AddonPaymentsConfig;
use Prestashop\Module\AddonPayments\Decorators\LegacyPaymentOption;
use Prestashop\Module\AddonPayments\Exception\CheckoutException;
use Prestashop\Module\AddonPayments\Helpers\SecurityTrait;
use PrestaShop\PrestaShop\Core\Payment\PaymentOption;
use PrestaShopLogger;

/**
 * Class PaymentOptionsFactory
 *
 * Handles payment option generation.
 */
class PaymentOptionsFactory
{
    use SecurityTrait;

    /**
     * @var \Context
     */
    protected $context;

    /**
     * @var \addonpayments
     */
    protected $module;

    /**
     * PaymentOptionsFactory constructor.
     *
     * @param \addonpayments $module
     */
    public function __construct(addonpayments $module)
    {
        $this->module = $module;
    }

    /**
     * Get available payment options.
     *
     * @return \PrestaShop\PrestaShop\Core\Payment\PaymentOption[]
     *
     * @throws \SmartyException
     */
    public function getOptions(): array
    {
        $this->getContext();
        return array_filter([$this->getRegisteredCustomerPaymentOption()]);
    }

    protected function isAnonymous(): bool
    {
        return !$this->getContext()->customer->isLogged();
    }

    protected function getContext(): Context
    {
        if (!$this->context) {
            $this->context = Context::getContext();
        }

        return $this->context;
    }

    public function setContext(Context $context): self
    {
        $this->context = $context;

        return $this;
    }

    /**
     * Get PS payment option for registered customers.
     *
     * @return \PrestaShop\PrestaShop\Core\Payment\PaymentOption|\Prestashop\Module\AddonPayments\Decorators\LegacyPaymentOption|null
     * @throws \SmartyException
     */
    protected function getRegisteredCustomerPaymentOption()
    {
        $idLanguage = $this->context->language->id;
        $addonPaymentsConfig = new AddonPaymentsConfig($this->module);
        $defaultConfiguration = Configuration::get(AddonPaymentsConfig::DEFAULT_CONFIGURATION);

        switch ($defaultConfiguration) {
            case '':
                $paymentTitle = 'Addon Payments';
                $paymentLogo = $addonPaymentsConfig->getDefaultLogoUri(). 'addonpayments.png';
                break;
            case AddonPaymentsConfig::DEFAULT_OPTION_CUSTOM:
                $paymentTitle = Configuration::get(AddonPaymentsConfig::TITLE, $idLanguage);
                $paymentLogo = Configuration::get(AddonPaymentsConfig::LOGO);
                $paymentLogo = !empty($paymentLogo) ? Media::getMediaPath($this->module->getLocalPath().'img'.DIRECTORY_SEPARATOR.$paymentLogo): '';
                break;
            default:
                $paymentTitle = $addonPaymentsConfig->getDefaultTitle($defaultConfiguration);
                $paymentLogo = $addonPaymentsConfig->getDefaultLogoUri(). $defaultConfiguration.'.png';
                break;
        }

        $operationType = ChargeTypeEnum::DEBIT;
        $customerId = ApiFactory::getCustomerIdFromContext($this->context);
        $instanceId = ApiFactory::getInstanceIdFromContext($this->context);
        $this->context->smarty->assign(
            [
                'instance_id' => $instanceId,
                'customer_id' => $customerId,
                'operation_type' => $operationType,
                'authorize_url' => $this->context->link->getModuleLink(
                    $this->module->name,
                    'authorize',
                    [
                        'instanceId' => $instanceId,
                        'customerId' => $customerId,
                        'operationType' => $operationType,
                        'action' => 'allPaysol',
                    ]
                ),
                'register_url' => $this->context->link->getModuleLink(
                    $this->module->name,
                    'vault',
                    [
                        'action' => 'checkoutRegister',
                        'ajax' => true,
                    ]
                ),
                'default_configuration' => $defaultConfiguration,
                'prepay_url' => $this->context->link->getModuleLink($this->module->name, 'validation'),
            ]
        );
        $template = $this->module->getLocalPath() . 'views/templates/hook/payment-options.tpl';
        $form = $this->context->smarty->fetch($template);

        return (new PaymentOption())
            ->setBinary(true)
            ->setModuleName($this->module->name . '_' . $instanceId)
            ->setForm($form)
            ->setLogo($paymentLogo)
            ->setCallToActionText($paymentTitle);
    }

    protected function getPaymentOptionName(): string
    {
        $paymentSolutions = $this->getAvailablePaymentSolutions();

        if (empty($paymentSolutions)) {
            return $this->module->displayName;
        }

        $names = array_map(
            function ($item) {
                /* @var \ComerciaGlobalPayments\AddonPayments\SDK\Response\PaymentSolution $item */
                return $this->getSolutionName($item->getName());
            },
            $paymentSolutions
        );
        sort($names);
        $last = array_pop($names);

        return sprintf($this->module->l('%s or %s', 'paymentoptionsfactory'), implode(', ', $names), $last);
    }

    /**
     * Fetch from API available payment solutions for current operation.
     */
    protected function getAvailablePaymentSolutions(): array
    {
        try {
            /** @var \ComerciaGlobalPayments\AddonPayments\SDK\Api\EPGJs $client */
            $client = ApiFactory::createInstance(EPGJs::NAME);
            $request = (new ExpressCheckout())
                ->setCountry($this->getContext()->country->iso_code)
                ->setCurrency($this->getContext()->currency->iso_code)
                ->setOperationType(ChargeTypeEnum::DEBIT)
                ->setProductId(Configuration::get(AddonPaymentsConfig::EPG_PRODUCT_ID));
            /** @var \ComerciaGlobalPayments\AddonPayments\SDK\Response\ExpressCheckout|\ComerciaGlobalPayments\AddonPayments\SDK\Response\ErrorResponse $response */
            $response = $client->expressCheckout($request);
            if ($response instanceof ErrorResponseInterface) {
                throw new CheckoutException($response->getReason(), CheckoutException::API_EXPRESS_CHECKOUT_FAILED);
            }

            return $response->getPaymentSolutions();
        } catch (CheckoutException $e) {
            PrestaShopLogger::addLog('Express checkout failed: ' . $e->getMessage(), 3);
        } catch (Exception $e) {
            PrestaShopLogger::addLog($e->getMessage(), 4);
        }

        return [];
    }

    /**
     * Get the payment solution human-friendly name.
     */
    public function getSolutionName(string $name): string
    {
        // @TODO This should be configurable.
        $names = [
            'CreditCards' => $this->module->l('credit cards', 'paymentoptionsfactory'),
        ];

        return $names[$name] ?? $name;
    }

    /**
     * Build a valid PS payment option by a payment solution definition from API.
     *
     * @param \ComerciaGlobalPayments\AddonPayments\SDK\Response\PaymentSolution $paymentSolution
     *
     * @return \PrestaShop\PrestaShop\Core\Payment\PaymentOption|\Prestashop\Module\AddonPayments\Decorators\LegacyPaymentOption|null
     *
     * @throws \SmartyException
     */
    protected function buildPaymentOption(PaymentSolution $paymentSolution)
    {
        $name = $this->getSolutionName($paymentSolution->getName());
        $operationType = ChargeTypeEnum::DEBIT;
        $customerId = ApiFactory::getCustomerIdFromContext($this->context);
        $instanceId = sha1($customerId . $paymentSolution->getName());
        $this->context->smarty->assign(
            [
                'instance_id' => $instanceId,
                'customer_id' => $customerId,
                'payment_solution' => $paymentSolution->getName(),
                'operation_type' => $operationType,
                'authorize_url' => $this->context->link->getModuleLink(
                    $this->module->name,
                    'authorize',
                    [
                        'instanceId' => $instanceId,
                        'customerId' => $customerId,
                        'operationType' => $operationType,
                        'paymentSolution' => $paymentSolution->getName(),
                        'action' => 'quickpay',
                    ]
                ),
                'prepay_url' => $this->context->link->getModuleLink($this->module->name, 'validation'),
            ]
        );
        $template = $this->module->getLocalPath() . 'views/templates/hook/payment-options--quickpay.tpl';
        $form = $this->context->smarty->fetch($template);

        if (addonpayments::is17()) {
            return (new PaymentOption())
                ->setBinary(true)
                ->setModuleName($this->module->name . '_' . $paymentSolution->getName())
                ->setForm($form)
                ->setLogo(Media::getMediaPath(_PS_MODULE_DIR_ . $this->module->name . '/views/img/logo17.png'))
                ->setCallToActionText(sprintf($this->module->l('Pay with %s', 'paymentoptionsfactory'), $name));
        }

        // PS 1.6
        /** @noinspection PhpUndefinedClassInspection */
        /** @noinspection PhpUndefinedMethodInspection */
        return (new LegacyPaymentOption())
            ->setBinary(true)
            ->setModuleName($this->module->name . '_' . $paymentSolution->getName())
            ->setForm($form)
            ->setCallToActionText(sprintf($this->module->l('Pay with %s', 'paymentoptionsfactory'), $name));
    }
}
