<?php

declare(strict_types=1);

namespace Paysera\CheckoutSdk\Service\Facade;

use Paysera\CheckoutSdk\Entity\CallbackEventInterface;
use Paysera\CheckoutSdk\Entity\PaymentCallback;
use Paysera\CheckoutSdk\Entity\RefundCallback;
use Paysera\CheckoutSdk\Exception\BaseException;
use Paysera\CheckoutSdk\Exception\CallbackBuildIntegrationException;
use Paysera\CheckoutSdk\Exception\CallbackVerificationIntegrationException;
use Paysera\CheckoutSdk\Exception\IntegrationException;
use Paysera\CheckoutSdk\Exception\UnsupportedCallbackIntegrationException;
use Paysera\CheckoutSdk\Service\PaymentApiCallbackBuilder;
use Paysera\CheckoutSdk\Service\PaymentApiCallbackVerifier;
use Paysera\CheckoutSdk\Service\Validator\CallbackEventValidator;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerInterface;

class Callbacks
{
    private LoggerInterface $logger;
    private PaymentApiCallbackVerifier $paymentApiCallbackVerifier;
    private PaymentApiCallbackBuilder $paymentApiCallbackBuilder;
    private CallbackEventValidator $callbackEventValidator;

    public function __construct(
        LoggerInterface $logger,
        PaymentApiCallbackVerifier $paymentApiCallbackVerifier,
        PaymentApiCallbackBuilder $paymentApiCallbackBuilder,
        CallbackEventValidator $callbackEventValidator
    ) {
        $this->logger = $logger;
        $this->paymentApiCallbackVerifier = $paymentApiCallbackVerifier;
        $this->paymentApiCallbackBuilder = $paymentApiCallbackBuilder;
        $this->callbackEventValidator = $callbackEventValidator;
    }

    /**
     * @throws CallbackVerificationIntegrationException
     * @throws CallbackBuildIntegrationException
     * @throws UnsupportedCallbackIntegrationException
     */
    public function processPaymentCallback(RequestInterface $request): PaymentCallback
    {
        $this->verifyCallback($request);
        $paymentCallback = $this->buildPaymentCallback($request);
        $this->checkCallbackSupport($paymentCallback->getEvent());

        return $paymentCallback;
    }

    /**
     * @throws IntegrationException
     * @deprecated need to be revised after implementation of refund callback
     */
    public function processRefundCallback(RequestInterface $request): RefundCallback
    {
        try {
            $this->paymentApiCallbackVerifier->verify($request);

            return $this->paymentApiCallbackBuilder->buildRefundCallback($request);
        } catch (BaseException $exception) {
            $this->logger->error(
                'Unable to process with refund callback request',
                [
                    'exception' => $exception,
                ]
            );

            throw new IntegrationException(
                'Unable to process with refund callback request',
                $exception->getCode(),
                $exception
            );
        }
    }

    /**
     * @throws CallbackVerificationIntegrationException
     */
    private function verifyCallback(RequestInterface $request): void
    {
        try {
            $this->paymentApiCallbackVerifier->verify($request);
        } catch (BaseException $exception) {
            $this->logger->error(
                'Unable to verify payment callback request',
                [
                    'exception' => $exception,
                ]
            );

            throw new CallbackVerificationIntegrationException(
                'Unable to verify payment callback request',
                $exception->getCode(),
                $exception
            );
        }
    }

    /**
     * @throws CallbackBuildIntegrationException
     */
    private function buildPaymentCallback(RequestInterface $request): PaymentCallback
    {
        try {
            return $this->paymentApiCallbackBuilder->buildPaymentCallback($request);
        } catch (BaseException $exception) {
            $this->logger->error(
                'Unable to build payment callback request',
                [
                    'exception' => $exception,
                ]
            );

            throw new CallbackBuildIntegrationException(
                'Unable to build payment callback request',
                $exception->getCode(),
                $exception
            );
        }
    }

    /**
     * @throws UnsupportedCallbackIntegrationException
     */
    private function checkCallbackSupport(CallbackEventInterface $callbackEvent): void
    {
        if ($this->callbackEventValidator->isSupported($callbackEvent)) {
            return;
        }

        $this->logger->warning(
            'Unsupported callback received',
            [
                'type' => $callbackEvent->getType(),
                'name' => $callbackEvent->getName(),
            ]
        );

        throw new UnsupportedCallbackIntegrationException('Unsupported callback received');
    }
}
