<?php

/**
 * 2018 Paysera
 *
 * 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.
 *
 * @author Paysera <plugins@paysera.com>
 * @copyright 2018 Paysera
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 * International Registered Trademark & Property of Paysera
 */
declare(strict_types=1);

if (!defined('_PS_VERSION_')) {
    exit;
}

use Paysera\Delivery\PayseraDeliveryCarriers;
use Paysera\Delivery\PayseraDeliveryConfiguration;
use Paysera\Delivery\Service\Provider\JSProvider;

class AdminPayseraDeliveryController extends ModuleAdminController
{
    private const GENERAL_TAB_KEY = 'general_setting';
    private const CARRIER_TAB_KEY = 'carrier_settings';
    /**
     * @var string
     */
    public $php_self;
    /**
     * @var string|null
     */
    public $projectHash;
    /**
     * @var string|null
     */
    private $selectedTab;
    /**
     * @var PayseraDeliveryConfiguration
     */
    private $configuration;
    /**
     * @var PayseraDeliveryCarriers
     */
    private $carriers;

    public function __construct()
    {
        parent::__construct();
        $this->bootstrap = true;
        $this->className = 'Configuration';
        $this->table = 'configuration';

        $this->selectedTab = null;
        $this->configuration = new PayseraDeliveryConfiguration();
        $this->carriers = new PayseraDeliveryCarriers($this->module, $this->configuration);

        $this->php_self = 'AdminPayseraDelivery';
        $this->projectHash = $this->configuration->getResolvedProjectId() ?? null;
    }

    public function renderList(): ?string
    {
        $this->carriers->setUpDefaultCarriersData();

        $templateData = [
            'settingsTitle' => $this->l('Configuration'),
            'configuration_tabs' => $this->getConfigurationTabs(),
            'selectedTab' => $this->getSelectedTab(),
            'backOfficeJsUrl' => __PS_BASE_URI__ . 'modules/' . $this->module->name . '/views/js/delivery.js',
            'backOfficeCssUrl' => __PS_BASE_URI__ . 'modules/' . $this->module->name . '/views/css/delivery.css',
            'imageDirectory' => _MODULE_DIR_ . $this->module->name . '/views/img/',
        ];

        if (isset($this->context->cookie->payseraConfigMessage)) {
            $templateData['message']['success'] = $this->context->cookie->payseraMessageSuccess;
            $templateData['message']['text'] = $this->context->cookie->payseraConfigMessage;
            unset($this->context->cookie->payseraConfigMessage);
        } else {
            $templateData['message'] = false;
        }

        $this->context->smarty->assign($templateData);

        return $this->module->display(
            _PS_MODULE_DIR_ . '/' . $this->module->name,
            'views/templates/admin/pages/configuration.tpl'
        );
    }

    /**
     * @return array<int, array<string, array<int, array<string, mixed>>|string>>
     */
    private function getConfigurationTabs(): array
    {
        $tabs = [];
        $tabs[] = [
            'id' => self::GENERAL_TAB_KEY,
            'title' => $this->l('Main Settings'),
            'content' => $this->getGeneralSettingTemplate(),
        ];

        if (
            !empty($this->configuration->getProjectId())
            && !empty($this->configuration->getProjectPassword())
            && !empty($this->configuration->getResolvedProjectId())
            && count($this->configuration->getDeliveryGateways()) > 0
        ) {
            $tabs[] = [
                'id' => self::CARRIER_TAB_KEY,
                'title' => $this->l('Carriers Settings'),
                'content' => $this->getCarriersSettingsTemplate(),
                'action_link' => $this->context->link->getAdminLink('AdminPayseraDelivery'),
            ];
        }

        return $tabs;
    }

    private function getSelectedTab(): string
    {
        if ($this->selectedTab !== null) {
            return $this->selectedTab;
        }

        if (Tools::getValue('selected_tab') !== false) {
            return (string) Tools::getValue('selected_tab');
        }

        return self::GENERAL_TAB_KEY;
    }

    private function getGeneralSettingTemplate(): string
    {
        $translations = $this->getGeneralSettingTranslations();

        if (Tools::isSubmit('btnGeneralSubmit') !== false) {
            $this->validateGeneralSetting();
            $this->selectedTab = self::GENERAL_TAB_KEY;
        }

        return $this->renderGeneralSettingForm($translations);
    }

    private function validateGeneralSetting(): void
    {
        if (!Tools::isSubmit('btnGeneralSubmit')) {
            return;
        }

        $warning = '';
        $projectId = trim((string) Tools::getValue(PayseraDeliveryConfiguration::PROJECT_ID));
        $projectSign = trim((string) Tools::getValue(PayseraDeliveryConfiguration::PROJECT_PASSWORD));

        if ($projectId === '' && $projectSign === '') {
            $warning = $this->l('Project ID and Sign are required, please fill out these fields.');
        } elseif ($projectId === '') {
            $warning = $this->l('Project ID is required, please fill out this field.');
        } elseif ($projectSign === '') {
            $warning = $this->l('Project Sign is required, please fill out this field.');
        } elseif (!is_numeric($projectId)) {
            $warning = $this->l('Project ID must consist of numbers only. Please provide a valid Project ID.');
        }

        $logger = new \Paysera\Delivery\Service\Logger();

        $configuration = new \Paysera\Delivery\PayseraDeliveryConfiguration;

        $clientFactory = new \Paysera\DeliveryApi\MerchantClient\ClientFactory([
            'base_url' => \Paysera\Delivery\Provider\MerchantClientFactoryProvider::BASE_URL,
            'mac' => [
                'mac_id' => $projectId,
                'mac_secret' => $projectSign,
            ],
        ]);

        $merchantClient = $clientFactory->getMerchantClient();

        $deliveryApiClient = new \Paysera\Delivery\Service\DeliveryApiClient($merchantClient, $logger, $configuration);
        $projectHash = $deliveryApiClient->getResolvedProjectId();

        if (empty($projectHash)) {
            $warning = $this->l('Invalid Project ID or password. Please provide valid credentials.');
        }
        $this->projectHash = $projectHash;

        if ($warning === '') {
            $this->updateGeneralSetting();
        } else {
            $this->context->cookie->payseraMessageSuccess = false;
            $this->context->cookie->payseraConfigMessage = $warning;
        }
    }

    /**
     * @return array<string, string|array<string, string>>
     */
    private function getGeneralSettingTranslations(): array
    {
        $translations = [];

        if ($this->l('PAYSERA_DELIVERY_PROJECT_ID') === 'PAYSERA_DELIVERY_PROJECT_ID') {
            $translations['project_id']['label'] = 'Project ID';
        } else {
            $translations['project_id']['label'] = $this->l('PAYSERA_DELIVERY_PROJECT_ID');
        }

        if ($this->l('PAYSERA_DELIVERY_SIGN_PASS') === 'PAYSERA_DELIVERY_SIGN_PASS') {
            $translations['sign_pass']['label'] = 'Sign';
        } else {
            $translations['sign_pass']['label'] = $this->l('PAYSERA_DELIVERY_SIGN_PASS');
        }

        if ($this->l('PAYSERA_DELIVERY_TEST_MODE') === 'PAYSERA_DELIVERY_TEST_MODE') {
            $translations['test']['label'] = 'Test';
        } else {
            $translations['test']['label'] = $this->l('PAYSERA_DELIVERY_TEST_MODE');
        }

        if ($this->l('PAYSERA_DELIVERY_PROJECT_ID_DESC') === 'PAYSERA_DELIVERY_PROJECT_ID_DESC') {
            $translations['project_id']['desc'] = 'Paysera project id';
        } else {
            $translations['project_id']['desc'] = $this->l('PAYSERA_DELIVERY_PROJECT_ID_DESC');
        }

        if ($this->l('PAYSERA_DELIVERY_SIGN_PASS_DESC') === 'PAYSERA_DELIVERY_SIGN_PASS_DESC') {
            $translations['sign_pass']['desc'] = 'Paysera project password';
        } else {
            $translations['sign_pass']['desc'] = $this->l('PAYSERA_DELIVERY_SIGN_PASS_DESC');
        }

        if ($this->l('BACKEND_GENERAL_SAVE') === 'BACKEND_GENERAL_SAVE') {
            $translations['save'] = 'Save main settings';
        } else {
            $translations['save'] = $this->l('BACKEND_GENERAL_SAVE');
        }

        $translations['test']['desc'] = '';

        return $translations;
    }

    private function updateGeneralSetting(): void
    {
        if (Tools::isSubmit('btnGeneralSubmit') !== false) {
            $this->configuration->setProjectId((string) Tools::getValue(PayseraDeliveryConfiguration::PROJECT_ID));
            $this->configuration->setProjectPassword((string) Tools::getValue(PayseraDeliveryConfiguration::PROJECT_PASSWORD));
            $this->configuration->setTestMode((bool) Tools::getValue(PayseraDeliveryConfiguration::TEST_MODE));
            $this->configuration->setResolvedProjectId($this->projectHash);
            if ($this->l('PAYSERA_SUCCESS_GENERAL_SETTING') === 'PAYSERA_SUCCESS_GENERAL_SETTING') {
                $successMessage = 'Your Paysera settings were successfully updated.';
            } else {
                $successMessage = $this->l('PAYSERA_SUCCESS_GENERAL_SETTING');
            }

            $this->carriers->setUpDefaultCarriersData();

            $this->context->cookie->payseraMessageSuccess = true;
            $this->context->cookie->payseraConfigMessage = $successMessage;

            Tools::redirectAdmin(
                $this->context->link->getAdminLink('AdminPayseraDelivery') . '&success=1'
            );
        }
    }

    /**
     * @param array<string, string|array<string, string>> $translations
     *
     * @return string
     */
    private function renderGeneralSettingForm(array $translations): string
    {
        $helper = new HelperForm();
        $helper->show_toolbar = false;
        $helper->table = $this->table;
        $helper->default_form_language = (new Language((int) Configuration::get('PS_LANG_DEFAULT')))->id;
        $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ?
            Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0
        ;
        $this->fields_form = [];
        $this->fields_form = $this->getGeneralSettingForm($translations);
        $helper->id = (int) Tools::getValue('id_carrier');
        $helper->identifier = $this->identifier;
        $helper->submit_action = 'btnGeneralSubmit';
        $helper->currentIndex = $this->context->link->getAdminLink('AdminPayseraDelivery');
        $helper->tpl_vars = [
            'fields_value' => $this->getGeneralSetting(),
            'languages' => $this->getLanguages(),
            'id_language' => $this->context->language->id,
        ];

        return $helper->generateForm($this->fields_form)
            . (new JSProvider())->getNumberOnlyInputEventListener('#' . PayseraDeliveryConfiguration::PROJECT_ID);
    }

    /**
     * @param array<string, string|array<string, string>> $translations
     *
     * @return array<int, array<string, mixed>>
     */
    private function getGeneralSettingForm(array $translations): array
    {
        $generalForm = [];
        $generalForm[] = [
            'form' => [
                'col' => '6',
                'input' => [
                    $this->getTextForm(
                        PayseraDeliveryConfiguration::PROJECT_ID,
                        is_array($translations['project_id']) ? $translations['project_id'] : [],
                        true
                    ),
                    $this->getTextForm(
                        PayseraDeliveryConfiguration::PROJECT_PASSWORD,
                        is_array($translations['sign_pass']) ? $translations['sign_pass'] : [],
                        true
                    ),
                    $this->getBoolForm(
                        PayseraDeliveryConfiguration::TEST_MODE,
                        is_array($translations['test']) ? $translations['test'] : []
                    ),
                ],
                'submit' => [
                    'title' => $translations['save'],
                ],
            ],
        ];

        return $generalForm;
    }

    private function getOrderStates()
    {
        return OrderState::getOrderStates((int) $this->context->language->id);
    }

    /**
     * @return array<string, mixed>
     */
    private function getGeneralSetting(): array
    {
        return [
            PayseraDeliveryConfiguration::PROJECT_ID => Tools::getValue(PayseraDeliveryConfiguration::PROJECT_ID, $this->configuration->getProjectId()),
            PayseraDeliveryConfiguration::PROJECT_PASSWORD => Tools::getValue(
                PayseraDeliveryConfiguration::PROJECT_PASSWORD,
                $this->configuration->getProjectPassword()
            ),
            PayseraDeliveryConfiguration::TEST_MODE => Tools::getValue(PayseraDeliveryConfiguration::TEST_MODE, $this->configuration->isTestModeEnabled()),
        ];
    }

    /**
     * @param string $fieldName
     * @param array<string, string> $translations
     * @param bool $isRequired
     *
     * @return array<string, mixed>
     */
    private function getTextForm(string $fieldName, array $translations, bool $isRequired = false): array
    {
        return [
            'type' => 'text',
            'label' => $translations['label'],
            'name' => $fieldName,
            'required' => $isRequired,
            'desc' => $translations['desc'],
        ];
    }

    /**
     * @param string $fieldName
     * @param array<string, string> $translations
     * @param bool $isRequired
     *
     * @return array<string, mixed>
     */
    private function getBoolForm(string $fieldName, array $translations, bool $isRequired = false): array
    {
        return [
            'type' => 'switch',
            'label' => $translations['label'],
            'name' => $fieldName,
            'required' => $isRequired,
            'desc' => $translations['desc'],
            'class' => 'fixed-width-xxl',
            'is_bool' => true,
            'values' => [
                [
                    'id' => 'active_on',
                    'value' => 1,
                ],
                [
                    'id' => 'active_off',
                    'value' => 0,
                ],
            ],
        ];
    }

    /**
     * @param string $fieldName
     * @param array<string, string> $translations
     * @param array $options
     * @param bool $isMultiple
     * @param bool $isRequired
     *
     * @return array<string, mixed>
     */
    private function getSelectForm(
        string $fieldName,
        array $translations,
        array $options,
        bool $isMultiple = false,
        bool $isRequired = false
    ): array {
        return [
            'type' => 'select',
            'label' => $translations['label'],
            'name' => $fieldName,
            'options' => $options,
            'multiple' => $isMultiple,
            'required' => $isRequired,
            'desc' => $translations['desc'],
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function getCarriersSettingsTemplate(): array
    {
        if (Tools::isSubmit('btnCarrierSubmit') !== false) {
            /** @var array<string, string> */
            $carriersPostData = Tools::getValue('carriers_post_data');
            $this->carriers->updateCarriersSavedData($carriersPostData);
            $this->selectedTab = self::CARRIER_TAB_KEY;
        }

        return $this->carriers->getCarriersSettingsFormData();
    }
}
