<?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\Payment\Sherlock2;
use CpCreation\VitiCore\Cart\Repository\PaymentMethodRepository;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Serializer\SerializerInterface;

final class Sherlock2Payment
{
    public const METHOD = 'sherlock2';
    public const URL = 'https://sherlocks-payment-webinit.secure.lcl.fr/paymentInit';
    public const URLTest = 'https://sherlocks-payment-webinit-simu.secure.lcl.fr/paymentInit';
    public const DEVISE = '978';

    /**
     * Contient uniquement la config du moyen de paiement "sherlock2"
     * Exemple attendu:
     * [
     *   'name' => 'sherlock2',
     *   'response_at' => 'route_name_response',
     *   'done' => 'route_name_done',
     * ].
     */
    private array $paths = [];

    public function __construct(
        private PaymentMethodRepository $paymentMethodRepository,
        private SerializerInterface $serializer,
        private RouterInterface $router,
        array $paths,
    ) {
        foreach ($paths as $path) {
            if (($path['name'] ?? null) === self::METHOD) {
                $this->paths = $path;
                break;
            }
        }
    }

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

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

        $data = [];
        $data['URL'] = $config->isProduction() ? self::URL : self::URLTest;

        $params = [];
        $returnParameters = $this->buildReturnParameters($payment);

        // URLs absolues (Symfony 7)
        $params['automaticResponseUrl'] = $this->router->generate(
            $this->paths['response_at'],
            [],
            RouterInterface::ABSOLUTE_URL
        );

        $params['normalReturnUrl'] = $this->router->generate(
            $this->paths['done'],
            $returnParameters,
            RouterInterface::ABSOLUTE_URL
        );

        $params['merchantId'] = $config->getMerchantId();
        $params['amount'] = $payment->getTotal();
        $params['currencyCode'] = self::DEVISE;

        $firstIntOfMerchantId = (int) ($config->getMerchantId()[0] ?? '0');
        if (0 === $firstIntOfMerchantId) {
            if (!$config->isProduction()) {
                $params['transactionReference'] = random_int(100000, 999999);
            } else {
                $params['s10TransactionReference.s10TransactionId'] = random_int(100000, 999999);
            }
        }

        $params['orderId'] = $payment->getToken();
        $params['keyVersion'] = $config->getKeyVersion();
        $params['customerContact.email'] = $payment->getEmail();
        $params['orderChannel'] = 'INTERNET';
        $params['transactionOrigin'] = 'SITE';

        $data['sealAlgorithm'] = 'SHA-256';

        $message = '';
        foreach ($params as $key => $value) {
            $message .= $key.'='.$value.'|';
        }
        $message = mb_substr($message, 0, -1);

        $data['Data'] = $message;
        $data['interfaceVersion'] = 'HP_2.42';
        $data['seal'] = mb_strtolower(hash('sha256', utf8_encode($message.$config->getSecretKey())));

        return $data;
    }

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

        return [];
    }

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

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

        $data = ['Valid' => false];

        if (!isset($response['Data'], $response['Seal'])) {
            return $data;
        }

        $responseExplode = explode('|', $response['Data']);
        $seal = mb_strtolower(hash('sha256', utf8_encode($response['Data'].$config->getSecretKey())));

        foreach ($responseExplode as $value) {
            $tmp = explode('=', $value, 2);
            if (!isset($tmp[0], $tmp[1])) {
                continue;
            }
            $data[$tmp[0]] = $tmp[1];
        }

        if (
            ($data['acquirerResponseCode'] ?? null) === '00'
            && ($data['responseCode'] ?? null) === '00'
            && $seal === mb_strtolower((string) $response['Seal'])
        ) {
            $data['Valid'] = true;
        }

        return $data;
    }
}
