<?php

/**
 * Copyright 2024-2025 Simply.IN Sp. z o.o.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the EUPL-1.2 or later.
 * You may not use this work except in compliance with the Licence.
 *
 * Copy of the Licence is available at:
 * https://joinup.ec.europa.eu/software/page/eupl
 * It is bundled with this package in the file LICENSE.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Licence is distributed on an as is basis,
 * without warranties or conditions of any kind, either express or implied.
 * Check the Licence for the specific language governing permissions
 * and limitations under the License.
 *
 * @author   Simply.IN Sp. z o.o.
 * @copyright 2024-2025 Simply.IN Sp. z o.o.
 * @license   https://joinup.ec.europa.eu/software/page/eupl
 */

declare(strict_types=1);

namespace SimplyIn\Install;

use Configuration;
use Context;
use Db;
use OrderState;
use Simplyin;
use SimplyIn\Api\SimplyInApiClientFactory;
use SimplyIn\Auth\RsaRevoker;
use SimplyIn\Auth\RsaSigner;
use SimplyIn\Auth\SecretCipher;
use SimplyIn\BackOffice\ConfigurationRepository;
use SimplyIn\BackOffice\Environment;
use SimplyIn\Webhook\WebhookDeduplicator;
use SimplyInEmailCache;
use SimplyInOrder;
use SimplyInOrderSync;
use SimplyInPayments;
use SimplyInPoint;
use Validate;

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

final class Uninstaller
{
    public function __construct(private readonly Simplyin $module)
    {
    }

    public function uninstallAll(): bool
    {
        $this->revokeRsaRegistration();
        $this->uninstallSql();
        $this->uninstallConfiguration();
        $this->softDeleteOrderStates();

        return $this->unregisterHooks();
    }

    /**
     * Remove every SIMPLYIN_* configuration row, including credentials (API key, RSA material,
     * webhook HMAC secret). Leaving them behind means an uninstalled module keeps live secrets
     * in the database.
     */
    public function uninstallConfiguration(): void
    {
        $rows = Db::getInstance()->executeS(
            'SELECT DISTINCT `name` FROM `' . _DB_PREFIX_ . "configuration` WHERE `name` LIKE 'SIMPLYIN\\_%'",
        );
        if (!is_array($rows)) {
            return;
        }
        foreach ($rows as $row) {
            if (isset($row['name'])) {
                Configuration::deleteByName((string) $row['name']);
            }
        }
    }

    /**
     * softDelete() (not delete()) — a hard delete drops the state row and its lang labels, which
     * would strip the status name from orders that already used it. Soft-deleting hides the state
     * from the dropdowns while preserving order history.
     */
    public function softDeleteOrderStates(): void
    {
        foreach (['SIMPLYIN_PAYMENT_STATUS_PENDING', 'SIMPLYIN_PAYMENT_STATUS_CANCELED'] as $key) {
            $id = (int) Configuration::get($key);
            if ($id <= 0) {
                continue;
            }
            $state = new OrderState($id);
            if (Validate::isLoadedObject($state)) {
                $state->softDelete();
            }
        }
    }

    /**
     * Best-effort: tell the backend to drop this shop's RSA public key registration before the
     * local credential is wiped, so an uninstall does not leave an orphaned, still-active
     * credential registered on the backend. Never blocks uninstall — see {@see RsaRevoker::revoke()}.
     *
     * Iterates BOTH environments, not just the currently selected one ({@see Simplyin::$rsaSigner}
     * / {@see Simplyin::$apiClient} only cover that one): a shop that registered in Production and
     * then switched to Test (or vice versa) can have a live registration in the other environment's
     * slot too, and {@see uninstallConfiguration()} wipes every SIMPLYIN_* row — including that
     * slot's key material — right after this runs, so it is now or never for revoking it.
     */
    public function revokeRsaRegistration(): void
    {
        $config = new ConfigurationRepository();
        $cipher = new SecretCipher(_COOKIE_KEY_);
        $origin = (string) Context::getContext()->shop->getBaseURL(true);

        foreach (Environment::cases() as $environment) {
            $signer = new RsaSigner($cipher, $environment->rsaSlotSuffix());
            if (!$signer->isRegistered()) {
                continue;
            }
            $apiClient = SimplyInApiClientFactory::create(
                baseUrl: rtrim($environment->backendUrl(), '/') . '/',
                merchantApiKey: $config->getApiKey($environment),
                origin: $origin,
                rsaSigner: $signer,
            );
            (new RsaRevoker($signer, $apiClient))->revoke();
        }
    }

    public function uninstallSql(): void
    {
        SimplyInOrder::uninstallSql();
        SimplyInPoint::uninstallSql();
        SimplyInOrderSync::uninstallSql();
        SimplyInEmailCache::uninstallSql();
        SimplyInPayments::uninstallSql();
        WebhookDeduplicator::uninstallSql();
    }

    public function unregisterHooks(): bool
    {
        foreach (HookRegistry::hooks() as $hook) {
            $this->module->unregisterHook($hook);
        }

        foreach (HookRegistry::legacyHooksToUnregister() as $hook) {
            $this->module->unregisterHook($hook);
        }

        return true;
    }
}
