<?php

/*
 * This file is part of the VitiCore 2.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Payment;

use CpCreation\VitiCore\Behavior\HasPayment;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\Payment\SystemPay;
use CpCreation\VitiCore\Cart\Repository\PaymentMethodRepository;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Serializer\SerializerInterface;

final class SystemPayPayment
{
    public const METHOD = 'systempay';
    public const DEVISE = 978;

    public function __construct(
        private PaymentMethodRepository $paymentMethodRepository,
        private SerializerInterface $serializer,
        private RouterInterface $router,
        private array $paths,
    ) {
    }

    public function createData(HasPayment $payment): array
    {
        $paymentMethod = $this->paymentMethodRepository->findOneBy(['method' => self::METHOD]);

        /** @var SystemPay $config */
        $config = $this->serializer->denormalize($paymentMethod->getConfiguration(), SystemPay::class);

        $hmac = $config->getKey();
        $hmacAlgo = $config->getHmacAlgo();

        $returnParameters = $this->buildReturnParameters($payment);

        $data = [];
        $data['URL'] = 'https://paiement.systempay.fr/vads-payment/';
        $data['vads_action_mode'] = 'INTERACTIVE';
        $data['vads_amount'] = $payment->getTotal();
        $data['vads_ctx_mode'] = $config->getMode();
        $data['vads_currency'] = self::DEVISE;
        $data['vads_cust_address'] = $payment->getStreet();
        $data['vads_cust_zip'] = $payment->getPostalCode();
        $data['vads_cust_city'] = $payment->getCity();
        $data['vads_cust_country'] = $payment->getCountry();
        $data['vads_cust_email'] = $payment->getEmail();

        // NB: je conserve la clé telle quelle (avec l'espace) pour ne pas casser un éventuel comportement,
        // mais c’est probablement une typo historique: 'vads_cust_cell_phone'
        $data['vads_cust_cell_phone '] = $payment->getPhone();

        $data['vads_cust_first_name'] = $payment->getFirstname();
        $data['vads_cust_last_name'] = $payment->getLastname();

        if ($payment->getCompany()) {
            $data['vads_cust_status'] = 'COMPANY';
            $data['vads_cust_legal_name'] = $payment->getCompany();
        } else {
            $data['vads_cust_status'] = 'PRIVATE';
        }

        $data['vads_order_id'] = $payment->getToken();
        $data['vads_page_action'] = 'PAYMENT';
        $data['vads_payment_config'] = 'SINGLE';
        $data['vads_site_id'] = $config->getSiteId();
        $data['vads_threeds_mpi'] = 4;
        $data['vads_trans_date'] = gmdate('YmdHis');
        $data['vads_trans_id'] = $payment->getToken();
        $data['vads_version'] = 'V2';

        // Symfony 7: URLs absolues via RouterInterface::ABSOLUTE_URL
        $data['vads_url_cancel'] = $this->router->generate(
            $this->paths[self::METHOD]['refused'],
            $returnParameters,
            UrlGeneratorInterface::ABSOLUTE_URL
        ); // Annulé

        $data['vads_url_return'] = $this->router->generate(
            $this->paths[self::METHOD]['refused'],
            $returnParameters,
            UrlGeneratorInterface::ABSOLUTE_URL
        ); // Retour boutique

        $data['vads_url_success'] = $this->router->generate(
            $this->paths[self::METHOD]['done'],
            $returnParameters,
            UrlGeneratorInterface::ABSOLUTE_URL
        ); // Paiement validé

        $data['vads_url_check'] = $this->router->generate(
            $this->paths[self::METHOD]['response_at'],
            [],
            UrlGeneratorInterface::ABSOLUTE_URL
        );

        ksort($data);

        $msg = '';
        foreach ($data as $key => $item) {
            if ('URL' !== $key) {
                $msg .= $item.'+';
            }
        }
        $msg .= $hmac;

        $signature = hash_hmac($hmacAlgo, $msg, $hmac);
        if ('sha256' === $hmacAlgo) {
            $signature = base64_encode(hash_hmac($hmacAlgo, $msg, $hmac, true));
        }
        $data['signature'] = $signature;

        return $data;
    }

    /**
     * @return array<string, string>
     */
    private function buildReturnParameters(HasPayment $payment): array
    {
        if ($payment instanceof Cart) {
            return ['cartId' => $payment->getId()->toString()];
        }

        return [];
    }

    public function valid(?array $response = null): bool
    {
        if (
            !\is_array($response)
            || !isset($response['vads_hash'], $response['vads_auth_result'], $response['signature'])
        ) {
            return false;
        }

        $auth = (string) $response['vads_auth_result'];
        if (!\in_array($auth, ['00', '08', '000', '001'], true)) {
            return false;
        }

        $paymentMethod = $this->paymentMethodRepository->findOneBy(['method' => self::METHOD]);

        /** @var SystemPay $config */
        $config = $this->serializer->denormalize($paymentMethod->getConfiguration(), SystemPay::class);

        $hmac = $config->getKey();
        $hmacAlgo = $config->getHmacAlgo();
        $signature = (string) $response['signature'];

        ksort($response);

        $msg = '';
        foreach ($response as $nom => $valeur) {
            if (str_starts_with((string) $nom, 'vads_')) {
                $msg .= (string) $valeur.'+';
            }
        }
        $msg .= $hmac;

        $signatureNew = hash_hmac($hmacAlgo, $msg, $hmac);
        if ('sha256' === $hmacAlgo) {
            $signatureNew = base64_encode(hash_hmac($hmacAlgo, $msg, $hmac, true));
        }

        return hash_equals($signatureNew, $signature);
    }
}
