<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Payments\Provider;

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Entity\Payment;
use Ferienpass\CoreBundle\Settings\IntegrationsManager;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class PmPaymentProvider implements PaymentProviderInterface
{
    public function __construct(private readonly HttpClientInterface $pmPaymentClient, private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $entityManager, private readonly IntegrationsManager $integrationsManager)
    {
    }

    public function dispatchPayment(Payment $payment): string
    {
        $config = $this->getConfig();
        $billingName = preg_split("/[\r\n\s]/", $payment->getBillingAddress() ?? '')[1] ?? '';

        $values = [
            'ags' => $config['pmPaymentAgs'],
            'amount' => $payment->getTotalAmount(),
            'procedure' => $config['pmPaymentProcedure'],
            'desc' => preg_replace('/[^a-zA-Z0-9\' ?.,\-()+\/]/', '', "{$config['pmPaymentProcedure']} {$payment->getId()} {$billingName}"),
            'accountingRecord' => \sprintf('%s|%s|%s', $config['pmPaymentProcedure'], $payment->getId(), preg_replace('/[\r\n]+/', '|', $payment->getBillingAddress() ?? '')),
            'notifyURL' => $this->urlGenerator->generate('_webhook_controller', ['type' => 'pmPayment'], UrlGeneratorInterface::ABSOLUTE_URL),
            'redirectURL' => $this->urlGenerator->generate('cms_pay_return', [], UrlGeneratorInterface::ABSOLUTE_URL),
        ];

        $response = $this->pmPaymentClient->request('POST', '/payment/secure', [
            'body' => $values + ['hash' => $this->calculateHash($values)],
        ]);

        $content = $response->toArray();
        if (null === ($redirect = $content['url'] ?? null)) {
            throw new ClientException($response);
        }

        $payment->setPmPaymentTransactionId($content['txid']);

        $this->entityManager->flush();

        return $redirect;
    }

    public function interpretRedirect(Request $request): int
    {
        $urlParams = [
            'ags' => $request->query->get('ags', ''),
            'txid' => $request->query->get('txid', ''),
            'amount' => $request->query->get('amount', 0),
            'desc' => $request->query->get('desc', ''),
            'status' => $request->query->get('status', ''),
            'payment_method' => $request->query->get('payment_method', ''),
            'created_at' => $request->query->get('created_at', ''),
        ];

        if (!$this->hashIsValid($request->query->get('hash', ''), $urlParams)) {
            return self::REDIRECT_UNKNOWN_ERROR;
        }

        if ('-1' === $urlParams['status'] && '' === $urlParams['payment_method']) {
            return self::REDIRECT_USER_CANCELLED;
        }

        if ('1' === $urlParams['status']) {
            return self::REDIRECT_PAYMENT_SUCCESS;
        }

        if ('-1' === $urlParams['status']) {
            return self::REDIRECT_PAYMENT_FAILED;
        }

        return self::REDIRECT_PAYMENT_VALIDATE;
    }

    public function isConfigured(): bool
    {
        return null !== $this->integrationsManager->getIntegrationConfig('PmPaymentProvider');
    }

    private function calculateHash(array $values): string
    {
        return hash_hmac('sha256', implode('|', $values), $this->getConfig()['pmPaymentSalt']);
    }

    private function getConfig(): array
    {
        return $this->integrationsManager->getIntegrationConfig('PmPaymentProvider') ?? throw new \RuntimeException('Payment provider PmPaymentProvider is not configured');
    }

    private function hashIsValid(string $hash, array $values): bool
    {
        return hash_equals($this->calculateHash($values), $hash);
    }
}
