<?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\MessageHandler;

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Entity\Payment;
use Ferienpass\CoreBundle\Entity\PmPaymentTransactionStatus;
use Ferienpass\CoreBundle\Enum\PaymentStatus;
use Ferienpass\CoreBundle\Message\FetchPmPaymentStatus as FetchPmPaymentStatusCommand;
use Ferienpass\CoreBundle\Repository\PaymentRepository;
use Ferienpass\CoreBundle\Settings\IntegrationsManager;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
use Symfony\Component\Workflow\WorkflowInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[AsMessageHandler(bus: 'command.bus')]
class FetchPmPaymentStatus
{
    public function __construct(private readonly HttpClientInterface $pmPaymentClient, private readonly EntityManagerInterface $entityManager, private readonly PaymentRepository $payments, private readonly WorkflowInterface $paymentStateMachine, private readonly IntegrationsManager $integrationsManager)
    {
    }

    public function __invoke(FetchPmPaymentStatusCommand $command): void
    {
        $config = $this->integrationsManager->getIntegrationConfig('PmPaymentProvider');
        if (null === $config) {
            return;
        }

        $txid = $command->getTxid();
        if ('' === $txid || '0' === $txid) {
            return;
        }

        $response = $this->pmPaymentClient->request('GET', '/payment/status/{ags}/{txid}', [
            'vars' => [
                'ags' => $config['pmPaymentAgs'],
                'txid' => $txid,
            ],
        ]);

        $content = $response->toArray();
        $isSuccessful = 1 === (int) $content['status'];
        $isPending = -1 === (int) $content['status'];

        /** @var Payment|null $payment */
        $payment = $this->payments->findOneBy(['pmPaymentTransactionId' => $txid]);

        $this->upsertStatus($txid, $content, $payment);

        // The webhook is unauthenticated and carries an attacker-controllable
        // txid, so an unknown/forged txid yields no Payment. upsertStatus() still
        // records the transaction status (null-safe); bail out before touching the
        // null Payment to avoid a fatal on every forged webhook (SECURITY_REVIEW FP-010).
        if (null === $payment) {
            return;
        }

        if (!$isPending && !$payment->isFinalized()) {
            try {
                $this->paymentStateMachine->apply($payment, $isSuccessful ? PaymentStatus::TRANSITION_PAY : PaymentStatus::TRANSITION_FAIL);
            } catch (NotEnabledTransitionException) {
            }
        }
    }

    public function upsertStatus(string $txid, array $content, ?Payment $payment): void
    {
        $repo = $this->entityManager->getRepository(PmPaymentTransactionStatus::class);
        /** @var PmPaymentTransactionStatus|null $entity */
        $entity = $repo->findOneBy(['txid' => $txid]);

        $status = $content['details']['status'] ?? null;
        $modifiedAt = $content['details']['timestamp'] ? new \DateTimeImmutable($content['details']['timestamp']) : null;
        $createdAt = $content['details']['transactions'][0]['timestamp'] ?? null ? new \DateTimeImmutable($content['details']['transactions'][0]['timestamp']) : null;

        if (null !== $entity) {
            $entity->setStatus($status);
            $entity->setDescription($content['details']['description'] ?? $content['description'] ?? null);
            $entity->setCreatedAt($createdAt);
            $entity->setModifiedAt($modifiedAt);
            $entity->setPayload($content);

            return;
        }

        $entity = new PmPaymentTransactionStatus(
            $txid,
            $status,
            $content['details']['description'] ?? $content['description'] ?? null,
            $createdAt,
            $modifiedAt,
            $content
        );

        $this->entityManager->persist($entity);
        $payment?->setPmPaymentTransactionStatus($entity);
    }
}
