<?php
/**
 * _             _       _
 * | |____      _| | __ _| |__
 * | '_ \ \ /\ / / |/ _` | '_ \
 * | |_) \ V  V /| | (_| | |_) |
 * |_.__/ \_/\_/ |_|\__,_|_.__/.
 *
 * @author    Bwlab di Luigi Massa <info@bwlab.it>
 * @copyright since 2014 Bwlab
 * @license   proprietary
 */

declare(strict_types=1);

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

require_once __DIR__ . '/vendor/autoload.php';

use Bwlab\Withdrawal\Hook\CheckoutHookHandler;
use Bwlab\Withdrawal\Hook\DashboardHookHandler;
use Bwlab\Withdrawal\Hook\OrderDetailHookHandler;

class Bwwithdrawal extends Module
{
    public const HOOKS = [
        'displayOrderDetail',
        'displayMyAccountOrder',
        'displayCheckoutBeforeConfirmation',
        'actionFrontControllerSetMedia',
        'actionAdminControllerSetMedia',
        'displayAdminOrdersView',
        'dashboardZoneOne',
    ];

    public $tabs = [
        [
            'route_name' => 'admin_bwwithdrawal_requests_index',
            'class_name' => 'AdminWithdrawalRequests',
            'visible' => true,
            'name' => 'Richieste Recesso',
            'parent_class_name' => 'AdminParentOrders',
            'wording' => 'Richieste Recesso',
            'wording_domain' => 'Modules.Bwwithdrawal.Admin',
        ],
    ];

    public function __construct()
    {
        $this->name = 'bwwithdrawal';
        $this->tab = 'checkout';
        $this->version = '1.0.0';
        $this->author = 'Bwlab';
        $this->need_instance = 0;
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->trans('Recesso Xpert', [], 'Modules.Bwwithdrawal.Admin');
        $this->description = $this->trans(
            'Funzione di recesso one-click conforme al D.Lgs. 209/2025 (art. 54-bis Codice del Consumo)',
            [],
            'Modules.Bwwithdrawal.Admin'
        );
        $this->confirmUninstall = $this->trans('Are you sure?', [], 'Modules.Bwwithdrawal.Admin');
        $this->ps_versions_compliancy = ['min' => '8.0.0', 'max' => '9.99.99'];

        // self-heal: re-register dashboardZoneOne if unhooked and keep it on top (module must be installed)
        if ((int) $this->id) {
            $this->ensureDashboardHookOnTop();
        }
    }

    /**
     * Ensures dashboardZoneOne is registered and positioned before every other module.
     * Idempotent: once this module is the lowest position it does nothing, so it is
     * safe to call on every constructor invocation.
     */
    private function ensureDashboardHookOnTop(): void
    {
        if (!$this->isRegisteredInHook('dashboardZoneOne')) {
            $this->registerHook('dashboardZoneOne');
        }

        $idHook = (int) Hook::getIdByName('dashboardZoneOne');
        if ($idHook === 0 || (int) $this->id === 0) {
            return;
        }

        $db = Db::getInstance();
        foreach (Shop::getContextListShopID() as $shopId) {
            $shopId = (int) $shopId;
            $minOther = $db->getValue(
                'SELECT MIN(position) FROM `' . _DB_PREFIX_ . 'hook_module`
                 WHERE id_hook = ' . $idHook . ' AND id_shop = ' . $shopId . '
                 AND id_module <> ' . (int) $this->id
            );
            if ($minOther === false || $minOther === null) {
                continue; // no other module on this hook for this shop
            }

            $myPos = (int) $db->getValue(
                'SELECT position FROM `' . _DB_PREFIX_ . 'hook_module`
                 WHERE id_hook = ' . $idHook . ' AND id_shop = ' . $shopId . '
                 AND id_module = ' . (int) $this->id
            );

            if ($myPos >= (int) $minOther) {
                if ((int) $minOther <= 0) {
                    // position is unsigned, can't go below 0: push every other module up by 1, take slot 0
                    $db->execute(
                        'UPDATE `' . _DB_PREFIX_ . 'hook_module` SET position = position + 1
                         WHERE id_hook = ' . $idHook . ' AND id_shop = ' . $shopId . '
                         AND id_module <> ' . (int) $this->id
                    );
                    $newPos = 0;
                } else {
                    $newPos = (int) $minOther - 1;
                }

                $db->execute(
                    'UPDATE `' . _DB_PREFIX_ . 'hook_module` SET position = ' . $newPos . '
                     WHERE id_hook = ' . $idHook . ' AND id_shop = ' . $shopId . '
                     AND id_module = ' . (int) $this->id
                );
            }
        }
    }

    public function install(): bool
    {
        if (!parent::install()
            || !$this->registerHook(self::HOOKS)
            || !$this->installSql()
            || !$this->installConfiguration()) {
            return false;
        }

        $this->ensureDashboardHookOnTop();

        return true;
    }

    public function uninstall(): bool
    {
        return parent::uninstall()
            && $this->uninstallConfiguration();
    }

    private function installSql(): bool
    {
        $sql = require __DIR__ . '/sql/install.php';
        foreach ($sql as $query) {
            if (!Db::getInstance()->execute($query)) {
                return false;
            }
        }

        return true;
    }

    private function installConfiguration(): bool
    {
        return Configuration::updateValue('BWWITHDRAWAL_INSTALL_DATE', time())
            && Configuration::updateValue('BWWITHDRAWAL_DEFAULT_DAYS', 14)
            && Configuration::updateValue('BWWITHDRAWAL_DATE_REF', 'order')
            && Configuration::updateValue('BWWITHDRAWAL_MERCHANT_EMAIL', Configuration::get('PS_SHOP_EMAIL'))
            && Configuration::updateValue('BWWITHDRAWAL_ENABLED', 1)
            && Configuration::updateValue('BWWITHDRAWAL_NOTICE_IT', $this->getDefaultNoticeIt())
            && Configuration::updateValue('BWWITHDRAWAL_NOTICE_EN', $this->getDefaultNoticeEn());
    }

    private function uninstallConfiguration(): bool
    {
        $keys = [
            'BWWITHDRAWAL_INSTALL_DATE',
            'BWWITHDRAWAL_DEFAULT_DAYS',
            'BWWITHDRAWAL_DATE_REF',
            'BWWITHDRAWAL_MERCHANT_EMAIL',
            'BWWITHDRAWAL_ENABLED',
            'BWWITHDRAWAL_NOTICE_IT',
            'BWWITHDRAWAL_NOTICE_EN',
        ];

        foreach ($keys as $key) {
            Configuration::deleteByName($key);
        }

        return true;
    }

    public function hookDisplayOrderDetail(array $params): string
    {
        /** @var OrderDetailHookHandler $handler */
        $handler = $this->get('bwwithdrawal.hook.order_detail');

        return $handler->handle($params);
    }

    public function hookDisplayMyAccountOrder(array $params): string
    {
        /** @var OrderDetailHookHandler $handler */
        $handler = $this->get('bwwithdrawal.hook.order_detail');

        return $handler->handleListItem($params);
    }

    public function getContent(): void
    {
        // modern config lives in a Symfony controller — redirect the legacy "Configure" button there
        $router = $this->get('router');
        Tools::redirectAdmin($router->generate('admin_bwwithdrawal_config'));
    }

    public function hookDisplayCheckoutBeforeConfirmation(array $params): string
    {
        /** @var CheckoutHookHandler $handler */
        $handler = $this->get('bwwithdrawal.hook.checkout');

        return $handler->handle($params);
    }

    public function hookDisplayAdminOrdersView(array $params): string
    {
        /** @var OrderDetailHookHandler $handler */
        $handler = $this->get('bwwithdrawal.hook.order_detail');

        return $handler->handleAdminView($params);
    }

    public function hookActionFrontControllerSetMedia(): void
    {
        $controller = $this->context->controller->php_self ?? '';
        if (!in_array($controller, ['order-detail', 'my-account'], true)) {
            return;
        }

        $this->context->controller->registerStylesheet(
            'bwwithdrawal-css',
            $this->_path . 'views/css/withdrawal.css',
            ['media' => 'all', 'priority' => 200]
        );
        $this->context->controller->registerJavascript(
            'bwwithdrawal-js',
            $this->_path . 'views/js/withdrawal.js',
            ['position' => 'bottom', 'priority' => 200]
        );

        Media::addJsDef([
            'bwwithdrawal' => [
                'ajaxUrl' => $this->context->link->getModuleLink('bwwithdrawal', 'withdrawal'),
                'errorMessage' => $this->trans('An error occurred. Please try again.', [], 'Modules.Bwwithdrawal.Front'),
            ],
        ]);
    }

    public function hookActionAdminControllerSetMedia(): void
    {
        // ponytail: reserved for future BO assets
    }

    public function hookDashboardZoneOne(array $params): string
    {
        /** @var DashboardHookHandler $handler */
        $handler = $this->get('bwwithdrawal.hook.dashboard');
        if (!$handler->shouldDisplay()) {
            return '';
        }

        $this->context->smarty->assign($handler->getTemplateVars());
        $this->context->smarty->assign([
            'bwwithdrawal_notice_img' => $this->_path . 'views/img/upgrade-notice.png',
            'bwwithdrawal_more_info_url' => 'https://www.bwlab.it/prestashop/aggiornare-a-prestashop-9',
        ]);

        return $this->fetch('module:bwwithdrawal/views/templates/hook/dashboard-zone-one.tpl');
    }

    public function isUsingNewTranslationSystem(): bool
    {
        return true;
    }

    private function getDefaultNoticeIt(): string
    {
        return 'Hai il diritto di recedere dal contratto entro 14 giorni senza alcuna penalità e senza specificarne il motivo. '
            . 'Puoi esercitare il diritto di recesso dalla tua area clienti, nella sezione "I miei ordini".';
    }

    private function getDefaultNoticeEn(): string
    {
        return 'You have the right to withdraw from this contract within 14 days without giving any reason. '
            . 'You may exercise the right of withdrawal from your customer account, in the "My orders" section.';
    }
}
