<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (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\Twig\Components\Payment;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\PaymentType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Manager\PaymentProviderManager;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Messenger\Messaging\CommandBusInterface;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Command\NotificationCommand;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Enum\PushLevel;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Notification\Enum\PushTransport;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Behavior\HasTemplateOverride;
use CpCreation\VitiCore\Cart\Model\PaymentMethod;
use CpCreation\VitiCore\Cart\Repository\PaymentMethodRepository;
use CpCreation\VitiCore\Contact\Model\Contact;
use CpCreation\VitiCore\Contact\Repository\ContactRepository;
use CpCreation\VitiCore\Payment\Model\Payment;
use CpCreation\VitiCore\Payment\Repository\PaymentRepository;
use CpCreation\VitiCore\Shop\Model\ShopConfig;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('payment:Form', template: '@components/payment/Form.html.twig')]
final class Form
{
    use ComponentWithFormTrait;
    use DefaultActionTrait;
    use HasTemplateOverride;

    #[LiveProp]
    public ?string $paymentId = null;

    private ?Payment $payment = null;

    public function __construct(
        private readonly FormFactoryInterface $formFactory,
        private readonly PaymentRepository $paymentRepository,
        private readonly PaymentMethodRepository $paymentMethodRepository,
        private readonly PaymentProviderManager $paymentProviderManager,
        private readonly ContactRepository $contactRepository,
        private readonly ShopConfigRepository $shopConfigRepository,
        private readonly RequestStack $requestStack,
        private readonly TranslatorInterface $translator,
        private readonly CommandBusInterface $commandSimpleBus,
    ) {
    }

    protected function instantiateForm(): FormInterface
    {
        return $this->formFactory->create(PaymentType::class, $this->resolvePayment());
    }

    public function getPaymentMethod(): ?PaymentMethod
    {
        return $this->paymentMethodRepository->findByDefaultForPaymentOnline();
    }

    public function getShopConfig(): ?ShopConfig
    {
        return $this->shopConfigRepository->findOne();
    }

    #[LiveAction]
    public function submitPayment(): ?Response
    {
        try {
            $this->submitForm();
        } catch (UnprocessableEntityHttpException) {
            $this->notifyFirstFormError();

            return null;
        }

        $paymentMethod = $this->getPaymentMethod();
        if (!$paymentMethod instanceof PaymentMethod) {
            $this->notify($this->translator->trans('payment.missing_method'), PushLevel::ERROR);

            return null;
        }

        $contact = $this->contactRepository->findOne();
        if (!$contact instanceof Contact) {
            $this->notify($this->translator->trans('payment.missing_contact'), PushLevel::ERROR);

            return null;
        }

        $payment = $this->getForm()->getData();
        if (!$payment instanceof Payment) {
            $this->notify($this->translator->trans('payment.error'), PushLevel::ERROR);

            return null;
        }

        try {
            $payment->setPaymentMethod($paymentMethod);
            $this->paymentRepository->save($payment);

            $this->requestStack->getSession()->set('payment', 'Payment');
            $this->requestStack->getSession()->set('payment_slug', $payment->getSlug());

            $response = $this->paymentProviderManager->createPaymentProviderManager($contact, $payment);
        } catch (\Throwable) {
            $this->notify($this->translator->trans('payment.error'), PushLevel::ERROR);

            return null;
        }

        return $response instanceof Response ? $response : null;
    }

    private function resolvePayment(): Payment
    {
        if ($this->payment instanceof Payment) {
            return $this->payment;
        }

        if (null === $this->paymentId || '' === mb_trim($this->paymentId)) {
            return $this->payment = new Payment();
        }

        $payment = $this->paymentRepository->find($this->paymentId);
        if (!$payment instanceof Payment) {
            throw new NotFoundHttpException('Paiement introuvable.');
        }

        return $this->payment = $payment;
    }

    private function notifyFirstFormError(): void
    {
        $error = $this->form?->getErrors(true)->current();
        if (null === $error) {
            return;
        }

        $this->notify($error->getMessage(), PushLevel::ERROR);
    }

    private function notify(string $message, PushLevel $level): void
    {
        $this->commandSimpleBus->dispatch(new NotificationCommand(
            message: $message,
            level: $level,
            transport: PushTransport::MERCURE,
        ));
    }
}
