<?php
/**
 * 2026 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 2026 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);

namespace Paysera\PrestashopPayments\Handler;

use Cart;
use Paysera\CheckoutSdk\Entity\PaymentLinkCreateRequest;
use Paysera\CheckoutSdk\Entity\PaymentLinkCreateResponse;
use Paysera\CheckoutSdk\Entity\PaymentOrderCreateRequest;
use Paysera\CheckoutSdk\Entity\PaymentStatus;
use Paysera\CheckoutSdk\Exception\IntegrationException;
use Paysera\CheckoutSdk\SdkFacade;
use Paysera\PrestashopPayments\Dto\PayseraPayment;
use Paysera\PrestashopPayments\Repository\PaymentStatusTransitionRepository;
use Paysera\PrestashopPayments\Repository\PayseraPaymentRepository;
use Paysera\PrestashopPayments\Service\OrderConverter;
use Paysera\PrestashopPayments\Service\PaymentMethodTitleResolver;
use Psr\Log\LoggerInterface;

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

class PaymentInitiationHandler
{
    public function __construct(
        private SdkFacade $sdkFacade,
        private OrderConverter $orderConverter,
        private PayseraPaymentRepository $payseraPaymentRepository,
        private PaymentStatusTransitionRepository $statusTransitionRepository,
        private PaymentMethodTitleResolver $paymentMethodTitleResolver,
        private LoggerInterface $logger
    ) {
    }

    /**
     * @phpstan-param Cart $cart
     */
    public function initiatePayment(\Cart $cart, \Order $order, string $paymentMethod = null, string $countryCode = null): string
    {
        $orderRequest = $this->orderConverter->buildPaymentOrderRequest($cart, $order);
        $linkRequestData = $this->orderConverter->buildPaymentLinkRequest($cart, $order, $paymentMethod, $countryCode);

        $this->logInitiation($cart, $paymentMethod, $countryCode, $orderRequest);

        try {
            return $this->performPaymentInitiation($cart, $order, $orderRequest, $linkRequestData, $paymentMethod);
        } catch (\Throwable $exception) {
            $this->handleException($cart, $exception);
        }
    }

    private function performPaymentInitiation(
        \Cart $cart,
        \Order $order,
        PaymentOrderCreateRequest $orderRequest,
        PaymentLinkCreateRequest $linkRequestData,
        ?string $paymentMethod
    ): string {
        $response = $this->sdkFacade
            ->getPaymentsFacade()
            ->initiatePayment($orderRequest, $linkRequestData)
        ;

        $this->logger->info('Payment initiated successfully', [
            'cart_id' => $cart->id,
            'order_id' => $response->getOrderId(),
            'link_id' => $response->getLinkId(),
        ]);

        $this->persistInitialPayment((int) $order->id, $orderRequest, $response, $paymentMethod);

        return $response->getPaymentUrl();
    }

    private function persistInitialPayment(
        int $orderId,
        PaymentOrderCreateRequest $orderRequest,
        PaymentLinkCreateResponse $response,
        ?string $paymentMethod
    ): void {
        $purchase = $orderRequest->getPurchase();

        $methodKey = $paymentMethod !== null && $paymentMethod !== '' ? $paymentMethod : null;

        if ($methodKey !== null) {
            $this->paymentMethodTitleResolver->resolve($methodKey);
        }

        $this->payseraPaymentRepository->save(new PayseraPayment(
            $orderId,
            null,
            $response->getOrderId(),
            $response->getPaymentUrl(),
            $purchase->getAmount(),
            $purchase->getCurrency(),
            null,
            null,
            null,
            null,
            $methodKey,
            PaymentStatus::INITIATED
        ));

        $this->statusTransitionRepository->append($orderId, PaymentStatus::INITIATED);
    }

    /**
     * @return never
     */
    private function handleException(\Cart $cart, \Throwable $exception): void
    {
        $this->logger->error('Payment initiation failed', [
            'cart_id' => $cart->id,
            'exception' => $exception->getMessage(),
            'exception_class' => get_class($exception),
            'exception_code' => $exception->getCode(),
        ]);

        throw new IntegrationException('Payment initiation failed: ' . $exception->getMessage(), 0, $exception);
    }

    private function logInitiation(
        \Cart $cart,
        ?string $paymentMethod,
        ?string $countryCode,
        PaymentOrderCreateRequest $orderRequest
    ): void {
        $this->logger->debug('Initiating payment for cart', [
            'cart_id' => $cart->id,
            'selected_payment_method' => $paymentMethod,
            'selected_country_code' => $countryCode,
            'purchase_amount' => $orderRequest->getPurchase()->getAmount(),
            'purchase_currency' => $orderRequest->getPurchase()->getCurrency(),
            'purchase_reference' => $orderRequest->getPurchase()->getReference(),
            'success_url' => $orderRequest->getRedirectUrls()?->getSuccessUrl(),
            'failure_url' => $orderRequest->getRedirectUrls()?->getFailureUrl(),
            'callback_url' => $orderRequest->getRedirectUrls()?->getCallbackUrl(),
        ]);
    }
}
