<?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\Cawl;
use CpCreation\VitiCore\Cart\Repository\PaymentMethodRepository;
use OnlinePayments\Sdk\Authentication\V1HmacAuthenticator;
use OnlinePayments\Sdk\Client;
use OnlinePayments\Sdk\Communicator;
use OnlinePayments\Sdk\CommunicatorConfiguration;
use OnlinePayments\Sdk\Domain\AmountOfMoney;
use OnlinePayments\Sdk\Domain\CardPaymentMethodSpecificInputBase;
use OnlinePayments\Sdk\Domain\CreateHostedCheckoutRequest;
use OnlinePayments\Sdk\Domain\Feedbacks;
use OnlinePayments\Sdk\Domain\HostedCheckoutSpecificInput;
use OnlinePayments\Sdk\Domain\MobilePaymentMethodHostedCheckoutSpecificInput;
use OnlinePayments\Sdk\Domain\Order;
use OnlinePayments\Sdk\Domain\OrderReferences;
use OnlinePayments\Sdk\Domain\RedirectPaymentMethodSpecificInput;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Serializer\SerializerInterface;

final class CawlPayment
{
    public const METHOD = 'cawl';
    public const TEST_URL = 'https://payment.preprod.cawl-solutions.fr/';
    public const PROD_URL = 'https://payment.cawl-solutions.fr/';
    public const DEVISE = 978;
    public const CURRENCY = 'EUR';

    /** @var array<string, mixed> */
    private array $paths;

    private ?Client $client = null;
    private ?string $pspid = null;
    private ?Cawl $config = null;

    public function __construct(
        private readonly PaymentMethodRepository $paymentMethodRepository,
        private readonly SerializerInterface $serializer,
        private readonly RouterInterface $router,
        array $paths,
    ) {
        // Chez toi tu appelles $this->paths['cawl']['done'] / ['response_at']
        // donc on garde l’array tel quel.
        $this->paths = $paths;
    }

    private function boot(): void
    {
        if (null !== $this->client && null !== $this->pspid && null !== $this->config) {
            return;
        }

        $paymentMethod = $this->paymentMethodRepository->findOneBy(['method' => self::METHOD]);
        if (!$paymentMethod) {
            throw new \RuntimeException('Payment method "cawl" introuvable.');
        }

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

        $apiEndpoint = $config->isProduction() ? self::PROD_URL : self::TEST_URL;

        $communicatorConfiguration = new CommunicatorConfiguration(
            $config->getApiKey(),
            $config->getApiSecret(),
            $apiEndpoint,
            'CPCREATION',
            null
        );

        $authenticator = new V1HmacAuthenticator($communicatorConfiguration);
        $communicator = new Communicator($communicatorConfiguration, $authenticator);

        $this->client = new Client($communicator);
        $this->pspid = $config->getPspId();
    }

    /**
     * Garde ta méthode existante (optionnelle).
     *
     * @return array{client: Client, pspid: string, config: Cawl}
     */
    public function createPaymentMandatoryObjects(): array
    {
        $this->boot();

        return [
            'client' => $this->client,
            'pspid' => $this->pspid,
            'config' => $this->config,
        ];
    }

    /**
     * @return array{URL: string}
     */
    public function createData(HasPayment $payment): array
    {
        $this->boot();
        $returnParameters = $this->buildReturnParameters($payment);

        $createHostedCheckoutRequest = new CreateHostedCheckoutRequest();

        $amountOfMoney = new AmountOfMoney();
        $amountOfMoney->setAmount($payment->getTotal());
        $amountOfMoney->setCurrencyCode(self::CURRENCY);

        $order = new Order();
        $order->setAmountOfMoney($amountOfMoney);

        $orderReferences = new OrderReferences();
        $orderReferences->setMerchantReference($payment->getToken());
        $order->setReferences($orderReferences);

        $returnRoute = $this->paths['cawl']['done'] ?? null;
        $webhookRoute = $this->paths['cawl']['response_at'] ?? null;

        if (!\is_string($returnRoute) || '' === $returnRoute) {
            throw new \RuntimeException('Route "cawl.done" (paths[cawl][done]) manquante.');
        }
        if (!\is_string($webhookRoute) || '' === $webhookRoute) {
            throw new \RuntimeException('Route "cawl.response_at" (paths[cawl][response_at]) manquante.');
        }

        $hostedCheckoutSpecificInput = new HostedCheckoutSpecificInput();
        $hostedCheckoutSpecificInput->setReturnUrl(
            $this->router->generate($returnRoute, $returnParameters, UrlGeneratorInterface::ABSOLUTE_URL)
        );

        $feedbacks = new Feedbacks();
        $feedbacks->setWebhooksUrls([
            $this->router->generate($webhookRoute, [], UrlGeneratorInterface::ABSOLUTE_URL),
        ]);

        $cardPaymentMethodSpecificInput = new CardPaymentMethodSpecificInputBase();
        $cardPaymentMethodSpecificInput->setAuthorizationMode('SALE');

        $mobilePaymentMethodSpecificInput = new MobilePaymentMethodHostedCheckoutSpecificInput();
        $mobilePaymentMethodSpecificInput->setAuthorizationMode('SALE');

        $redirectPaymentMethodSpecificInput = new RedirectPaymentMethodSpecificInput();
        $redirectPaymentMethodSpecificInput->setRequiresApproval(false);

        $createHostedCheckoutRequest->setOrder($order);
        $createHostedCheckoutRequest->setFeedbacks($feedbacks);
        $createHostedCheckoutRequest->setCardPaymentMethodSpecificInput($cardPaymentMethodSpecificInput);
        $createHostedCheckoutRequest->setMobilePaymentMethodSpecificInput($mobilePaymentMethodSpecificInput);
        $createHostedCheckoutRequest->setRedirectPaymentMethodSpecificInput($redirectPaymentMethodSpecificInput);
        $createHostedCheckoutRequest->setHostedCheckoutSpecificInput($hostedCheckoutSpecificInput);

        $response = $this->client
            ->merchant($this->pspid)
            ->hostedCheckout()
            ->createHostedCheckout($createHostedCheckoutRequest);

        // (Optionnel) tu le faisais déjà
        $response->setHostedCheckoutId(mb_strtoupper($payment->getToken()));

        return [
            'URL' => $response->getRedirectUrl(),
        ];
    }

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

        return [];
    }

    public function getStatus(string $hostedCheckoutId): mixed
    {
        $this->boot();

        return $this->client
            ->merchant($this->pspid)
            ->hostedCheckout()
            ->getHostedCheckout($hostedCheckoutId);
    }

    public function valid(string $rawJson, string $signature): bool
    {
        $this->boot();

        $secretKey = $this->config->getWebhookSecret();
        $expected = base64_encode(hash_hmac('sha256', $rawJson, $secretKey, true));

        if (!hash_equals($expected, $signature)) {
            return false;
        }

        $result = json_decode($rawJson, true);
        if (!\is_array($result)) {
            return false;
        }

        $status = $result['payment']['status'] ?? null;

        return 'CAPTURED' === $status;
    }
}
