<?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\Bootstrap;

use Context;
use Simplyin;
use SimplyIn\Api\EncryptionService;
use SimplyIn\Api\SimplyInApiClient;
use SimplyIn\Api\SimplyInApiClientFactory;
use SimplyIn\Auth\RsaRegistrar;
use SimplyIn\Auth\SecretCipher;
use SimplyIn\BackOffice\AdminOrderDisplay;
use SimplyIn\BackOffice\ConfigurationRepository;
use SimplyIn\BackOffice\OrdersGridDecorator;
use SimplyIn\BackOffice\OrderStatusSync;
use SimplyIn\Carrier\CarrierConfigRegistry;
use SimplyIn\Checkout\AccountUpdateHandler;
use SimplyIn\Checkout\CheckoutSessionRepository;
use SimplyIn\Checkout\LegacyCheckoutApi;
use SimplyIn\Checkout\LegacyEmailCache;
use SimplyIn\Checkout\LegacyOrderDataFetcher;
use SimplyIn\Checkout\PrestaShopCustomerEmailProvider;
use SimplyIn\Front\AssetRegistrar;
use SimplyIn\Front\JsHeaderDataBuilder;
use SimplyIn\Front\OpcDetector;
use SimplyIn\Payment\OrderConfirmationRenderer;
use SimplyIn\Payment\OrderRetryRenderer;
use SimplyIn\Payment\OrderStateUpdater;
use SimplyIn\Payment\PaymentLogoResolver;
use SimplyIn\Payment\PaymentMethodsProvider;
use SimplyIn\Payment\PaymentOptionsBuilder;
use SimplyIn\Payment\PaymentReconciliationSweep;
use SimplyIn\Payment\PaymentStatusChecker;
use SimplyIn\Payment\PaymentStatusReconciler;
use SimplyIn\Payment\PaywallRenderer;
use SimplyIn\Security\TokenService;
use SimplyIn\Service\EncryptedTrackingSender;
use SimplyIn\Service\MerchantSettingsSync;
use SimplyIn\Service\OrderSync\LegacyOrderSyncQueue;
use SimplyIn\Service\OrderSync\OrderCreationHandler;
use SimplyIn\Service\OrderSync\OrderSyncService;
use SimplyIn\Service\TrackingNumberSync;
use SimplyIn\Session\PrestashopSessionAdapter;
use SimplyIn\Session\SessionHelper;
use SimplyIn\Webhook\PrestaShopWebhookContextScoper;
use SimplyIn\Webhook\WebhookSecretStore;

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

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

    public function bootstrap(): void
    {
        $module = $this->module;
        $context = Context::getContext();

        $configurationRepository = new ConfigurationRepository();
        $environment = $configurationRepository->getEnvironment();
        $module->setEnvironment($environment);
        // A shop mid-adoption (see ConfigurationRepository::isEnvironmentAdoptionPending()) has no
        // resolved environment yet — $environment above is only the safe-default READ, not a
        // decision. Sending real API traffic under that guess is exactly the "live shop silently
        // talking to the wrong backend" risk this whole feature exists to prevent, so this request
        // is treated as unauthenticated instead: every authenticated call site already requires a
        // non-empty key (SimplyInApiClient::request() throws ApiException::missingApiKey()
        // otherwise), so this fails closed rather than guessing. Simplyin::attemptEnvironmentAdoption()
        // resolves this in the background; once it does, this branch never triggers again.
        $module->merchantApiKey = $configurationRepository->isEnvironmentAdoptionPending()
            ? ''
            : $configurationRepository->getApiKey($environment);
        $module->merchand_api_key = $module->merchantApiKey;

        $sessionAdapter = new PrestashopSessionAdapter($context->cookie, $context->cart);
        $module->simplyin_session = $sessionAdapter;

        // RSA request auth (clean RSA-only). One shared signer/registrar so state (memory cache,
        // once-per-process guard) stays consistent across every API client built this request.
        // The signer itself comes from setEnvironment() above — its storage is environment-scoped,
        // so that is the single place allowed to build one.
        $cipher = new SecretCipher(_COOKIE_KEY_);
        $rsaSigner = $module->rsaSigner;
        $rsaRegistrar = new RsaRegistrar(
            $rsaSigner,
            rtrim((string) $module->backend_url, '/') . '/',
            $module->merchantApiKey,
            (string) $context->shop->getBaseURL(true),
        );
        $module->rsaRegistrar = $rsaRegistrar;

        $module->apiClient = SimplyInApiClientFactory::fromPrestaShop($module, $context, null, $rsaSigner, $rsaRegistrar);

        $clientFactory = static function (?string $override) use ($module, $rsaSigner, $rsaRegistrar): SimplyInApiClient {
            $hasOverride = $override !== null && $override !== '';

            // A NEW/override API key (admin testing a key) must authenticate with THAT key, not with
            // an RSA credential registered against the current one — so skip the signer in that case.
            return SimplyInApiClientFactory::fromPrestaShop(
                $module,
                Context::getContext(),
                $hasOverride ? $override : null,
                $hasOverride ? null : $rsaSigner,
                $hasOverride ? null : $rsaRegistrar,
            );
        };

        $module->merchantSettingsSync = new MerchantSettingsSync(
            (string) $module->version,
            $clientFactory,
            new WebhookSecretStore($cipher),
        );
        if ($module->merchantApiKey !== '') {
            $module->simplyInUpdateMerchantSettings($module->merchantApiKey);
        }

        $module->opcDetector = new OpcDetector();
        $module->assetRegistrar = new AssetRegistrar($module->opcDetector);
        $module->tokenService = new TokenService(
            $module->name,
            (string) $module->version,
            _COOKIE_KEY_,
        );
        $module->jsHeaderDataBuilder = new JsHeaderDataBuilder(
            $module->opcDetector,
            $module->tokenService,
            $sessionAdapter,
            $configurationRepository,
        );
        // Built here, ahead of its other uses below, so the SAME instance backs every consumer of
        // an order-state write this request: the confirmation-page poller, the cron reconciliation
        // sweep, and (further down) the webhook and account-update paths.
        $module->orderStateUpdater = new OrderStateUpdater();

        // Shared by the confirmation-page poller (PaymentStatusChecker) and the cron reconciliation
        // sweep (PaymentReconciliationSweep) - one place asks SIN.CORE for an attempt's status and
        // applies the answer, so neither caller can end up deciding an outcome on its own.
        $paymentStatusReconciler = new PaymentStatusReconciler($module->apiClient, $module->orderStateUpdater);
        $module->paymentStatusChecker = new PaymentStatusChecker($paymentStatusReconciler);
        $module->paymentReconciliationSweep = new PaymentReconciliationSweep(
            $paymentStatusReconciler,
            new PrestaShopWebhookContextScoper(),
        );

        $logoResolver = new PaymentLogoResolver(dirname(__DIR__, 2) . '/views/img/providers');
        $paymentMethodsProvider = new PaymentMethodsProvider($module->apiClient, $sessionAdapter);
        // One paywall renderer for the request: the checkout's payment option and the surface that
        // re-offers payment on an unpaid order render the SAME paywall through it.
        $paywallRenderer = new PaywallRenderer($sessionAdapter);
        $module->paymentOptionsBuilder = new PaymentOptionsBuilder(
            $logoResolver,
            $paymentMethodsProvider,
            $paywallRenderer,
        );
        $module->orderRetryRenderer = new OrderRetryRenderer(
            $paymentMethodsProvider,
            $paywallRenderer,
            $module->tokenService,
            $logoResolver,
        );
        $module->orderConfirmationRenderer = new OrderConfirmationRenderer($sessionAdapter, $module->orderRetryRenderer);
        $module->checkoutSessionRepository = new CheckoutSessionRepository(
            $sessionAdapter,
            $module->opcDetector,
        );

        $module->orderSyncService = new OrderSyncService(
            $module->apiClient,
            $configurationRepository,
        );
        $module->orderCreationHandler = new OrderCreationHandler(
            $sessionAdapter,
            new LegacyCheckoutApi($module->apiClient),
            new LegacyOrderDataFetcher(),
            new PrestaShopCustomerEmailProvider(),
            new LegacyEmailCache(),
            new LegacyOrderSyncQueue($module->orderSyncService),
        );

        $encryptedTrackingSender = new EncryptedTrackingSender(
            new EncryptionService(),
            $module->apiClient,
            $module->merchantApiKey,
        );
        $module->trackingNumberSync = new TrackingNumberSync($encryptedTrackingSender);
        $module->carrierConfigRegistry = new CarrierConfigRegistry();
        $module->sessionHelper = new SessionHelper($sessionAdapter);

        $orderStatusSync = new OrderStatusSync($module->apiClient);
        $module->adminOrderDisplay = new AdminOrderDisplay($orderStatusSync);
        $module->accountUpdateHandler = new AccountUpdateHandler(
            $sessionAdapter,
            $module->checkoutSessionRepository,
        );
        $module->ordersGridDecorator = new OrdersGridDecorator($module);
    }
}
