<?php

declare(strict_types=1);

/*
 * 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\Controller\Site;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Withdrawal\Model\WithdrawalRequestData;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Withdrawal\WithdrawalRequestType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils\Mailer;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\WithdrawalRequest;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use CpCreation\VitiCore\Cart\Repository\WithdrawalRequestRepository;
use CpCreation\VitiCore\User\Model\User;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Translation\TranslatorInterface;

final class WithdrawalController extends AbstractController
{
    public function __construct(
        private readonly CartRepository $cartRepository,
        private readonly WithdrawalRequestRepository $withdrawalRequestRepository,
        private readonly Mailer $mailer,
        private readonly TranslatorInterface $translator,
        private readonly LoggerInterface $logger,
    ) {}

    #[Route(path: [
        'fr' => '/droit-de-retractation',
        'en' => '/en/withdraw-from-contract',
        'de' => '/de/vom-vertrag-zuruecktreten',
        'nl' => '/nl/overeenkomst-herroepen',
        'it' => '/it/recedere-dal-contratto',
        'es' => '/es/desistir-del-contrato',
    ], name: 'shop_withdrawal', methods: ['GET', 'POST'], options: ['sitemap' => false])]
    public function index(Request $request): Response
    {
        $data = new WithdrawalRequestData();
        $this->prefillData($data, $request);

        $form = $this->createForm(WithdrawalRequestType::class, $data);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $cart = $this->cartRepository->findCompletedOrderForWithdrawal(
                (string) $data->contractReference,
                (string) $data->receiptEmail,
            );

            if (!$cart instanceof Cart) {
                $message = $this->translator->trans('withdrawal.validation.order_not_found');
                $form->addError(new FormError($message));
                $this->addFlash('error', $message);
            } else {
                $withdrawalRequest = $cart->getWithdrawalRequest();
                $isNewRequest = !$withdrawalRequest instanceof WithdrawalRequest;

                if ($isNewRequest) {
                    $withdrawalRequest = new WithdrawalRequest(
                        cart: $cart,
                        firstname: (string) $data->firstname,
                        lastname: (string) $data->lastname,
                        contractReference: (string) $data->contractReference,
                        receiptEmail: (string) $data->receiptEmail,
                        orderReceived: true === $data->orderReceived,
                        receivedAt: true === $data->orderReceived ? $data->receivedAt : null,
                        locale: $request->getLocale(),
                        ipAddress: $request->getClientIp(),
                        userAgent: $request->headers->get('User-Agent'),
                        reasonCode: $data->reasonCode,
                        comment: $data->comment,
                    );
                    $this->withdrawalRequestRepository->save($withdrawalRequest);
                }

                if ($this->sendAcknowledgement($withdrawalRequest, $form)) {
                    if ($isNewRequest) {
                        $this->notifyShop($withdrawalRequest);
                    }

                    return $this->renderSuccess($withdrawalRequest);
                }
            }
        }

        return $this->render('withdrawal/index.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    private function prefillData(WithdrawalRequestData $data, Request $request): void
    {
        $reference = $request->query->getString('reference');
        if ('' !== $reference) {
            $data->contractReference = $reference;
        }

        $user = $this->getUser();
        if (!$user instanceof User || null === $user->getEmail()) {
            return;
        }

        $data->receiptEmail = $user->getEmail();

        if ('' === $reference) {
            return;
        }

        $cart = $this->cartRepository->findCompletedOrderForWithdrawal($reference, $user->getEmail());
        if (!$cart instanceof Cart) {
            return;
        }

        $data->firstname = $cart->getFirstname();
        $data->lastname = $cart->getLastname();
    }

    private function sendAcknowledgement(WithdrawalRequest $withdrawalRequest, FormInterface $form): bool
    {
        if ($withdrawalRequest->getAcknowledgedAt() instanceof \DateTimeImmutable) {
            return true;
        }

        try {
            $this->mailer->sendWithdrawalAcknowledgement($withdrawalRequest);
            $withdrawalRequest->markAcknowledged();
            $this->withdrawalRequestRepository->save($withdrawalRequest);

            return true;
        } catch (\Throwable $exception) {
            $this->logger->error('Echec de l\'accuse de reception de retractation.', [
                'withdrawal_request_id' => $withdrawalRequest->getId()->toString(),
                'exception' => $exception,
            ]);
            $form->addError(new FormError($this->translator->trans('withdrawal.validation.acknowledgement_failed')));

            return false;
        }
    }

    private function notifyShop(WithdrawalRequest $withdrawalRequest): void
    {
        try {
            $this->mailer->sendWithdrawalAdminNotification($withdrawalRequest);
        } catch (\Throwable $exception) {
            $this->logger->error('Echec de la notification boutique de retractation.', [
                'withdrawal_request_id' => $withdrawalRequest->getId()->toString(),
                'exception' => $exception,
            ]);
        }
    }

    private function renderSuccess(WithdrawalRequest $withdrawalRequest): Response
    {
        $response = $this->render('withdrawal/success.html.twig', [
            'withdrawal_request' => $withdrawalRequest,
        ]);
        $response->headers->set('Cache-Control', 'no-store, private');

        return $response;
    }
}
