<?php

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Controller\Admin;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Admin\PromoType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils\Mailer;
use CpCreation\VitiCore\Cart\Model\Promo;
use CpCreation\VitiCore\Cart\Repository\PromoRepository;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/code-promo')]
#[IsGranted(new Expression("is_granted('ROLE_ADMIN') or is_granted('ROLE_PROMO')"))]
final class PromoController extends AbstractController
{
    public function __construct(
        private readonly PromoRepository $promoRepository,
        private readonly ProductPriceGroupRepository $productPriceGroupRepository,
        private readonly Mailer $mailer,
    ) {}

    #[Route('', name: 'admin_promo')]
    public function index(Request $request): Response
    {
        $filters = $this->buildFilters($request);
        $promos = $this->promoRepository->findAdminFiltered(
            $filters['search'],
            $filters['application'],
            $filters['assignment'],
            $filters['benefit'],
            $filters['status'],
            $filters['origin'],
        );

        return $this->render('@CpCreationVitiCore/admin/promo/index.html.twig', [
            'items' => $promos,
            'filters' => $filters,
        ]);
    }

    #[Route('/new', name: 'admin_promo_new')]
    public function new(Request $request): Response
    {
        $promo = new Promo();
        $promo->setBeginAt(new \DateTimeImmutable());

        foreach ($this->productPriceGroupRepository->findByNameOrderAdmin() as $group) {
            if ($group instanceof ProductPriceGroup) {
                $promo->addGroup($group);
            }
        }

        $form = $this->createForm(PromoType::class, $promo);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /** @var Promo $data */
            $data = $form->getData();

            $this->promoRepository->save($data);
            $notificationSent = $this->notifyPromoRecipientIfNeeded($data, null);
            $this->addFlash('alert-success', 'Le code remise '.$data->getTitle().' a été ajouté avec succès');
            if ($notificationSent) {
                $this->addFlash('alert-success', 'L\'email d\'information du code promo a été envoyé.');
            }

            return $this->redirectToRoute('admin_promo');
        }

        return $this->render('@CpCreationVitiCore/admin/promo/new.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    #[Route('/{id}/edit', name: 'admin_promo_edit')]
    public function edit(Promo $promo, Request $request): Response
    {
        $form = $this->createForm(PromoType::class, $promo);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /** @var Promo $data */
            $data = $form->getData();

            $this->promoRepository->save($data);
            $this->addFlash('alert-success', 'Le code remise '.$promo->getTitle().' a été modifié avec succès');

            return $this->redirectToRoute('admin_promo');
        }

        return $this->render('@CpCreationVitiCore/admin/promo/edit.html.twig', [
            'item' => $promo,
            'form' => $form->createView(),
        ]);
    }

    #[Route('/{id}/delete', name: 'admin_promo_delete')]
    public function delete(Promo $promo): Response
    {
        $this->promoRepository->delete($promo);
        $this->addFlash('alert-success', 'Le code remise '.$promo->getTitle().' a été supprimé avec succès.');

        return $this->redirectToRoute('admin_promo');
    }

    #[Route('/{id}/disable', name: 'admin_promo_disable')]
    public function disable(Promo $promo): Response
    {
        $promo->setDisabled(true);
        $this->promoRepository->save($promo);

        $this->addFlash('alert-success', 'Le code remise '.$promo->getTitle().' a été dépublié.');

        return $this->redirectToRoute('admin_promo');
    }

    #[Route('/{id}/enable', name: 'admin_promo_enable')]
    public function enable(Promo $promo): Response
    {
        $promo->setDisabled(false);
        $this->promoRepository->save($promo);

        $this->addFlash('alert-success', 'Le code remise '.$promo->getTitle().' a été publié.');

        return $this->redirectToRoute('admin_promo');
    }

    private function notifyPromoRecipientIfNeeded(Promo $promo, ?array $previousFingerprint): bool
    {
        $currentFingerprint = $this->createNotificationFingerprint($promo);

        if (null === $currentFingerprint) {
            return false;
        }

        if (null !== $previousFingerprint && $previousFingerprint === $currentFingerprint) {
            return false;
        }

        $this->mailer->sendPromoAssignment($promo);

        return true;
    }

    /**
     * @return array<string, int|float|string|null>|null
     */
    private function createNotificationFingerprint(Promo $promo): ?array
    {
        $recipient = $promo->getRecipientEmail();

        if (null === $recipient || '' === mb_trim($recipient)) {
            return null;
        }

        return [
            'assignedUserId' => $promo->getAssignedUser()?->getId()->toString(),
            'notificationEmail' => $promo->getNotificationEmail(),
            'title' => $promo->getTitle(),
            'percent' => $promo->getPercent(),
            'amountTTC' => $promo->getAmountTTC(),
            'percentShippingCost' => $promo->getPercentShippingCost(),
            'offeredVariantId' => $promo->getOfferedVariant()?->getId()->toString(),
            'beginAt' => $promo->getBeginAt()?->format(\DateTimeInterface::ATOM),
            'endAt' => $promo->getEndAt()?->format(\DateTimeInterface::ATOM),
            'auto' => $promo->isAuto() ? '1' : '0',
        ];
    }

    /**
     * @return array{
     *     search: ?string,
     *     search_input: string,
     *     application: string,
     *     assignment: string,
     *     benefit: string,
     *     status: string,
     *     origin: string,
     *     query_parameters: array<string, string>
     * }
     */
    private function buildFilters(Request $request): array
    {
        $search = self::trimToNull($request->query->get('search'));
        $application = self::trimToNull($request->query->get('application')) ?? 'all';
        $assignment = self::trimToNull($request->query->get('assignment')) ?? 'all';
        $benefit = self::trimToNull($request->query->get('benefit')) ?? 'all';
        $status = self::trimToNull($request->query->get('status')) ?? 'all';
        $origin = self::trimToNull($request->query->get('origin')) ?? 'all';

        if (!\in_array($application, ['all', 'auto', 'manual'], true)) {
            $application = 'all';
        }

        if (!\in_array($assignment, ['all', 'user', 'notification', 'unassigned'], true)) {
            $assignment = 'all';
        }

        if (!\in_array($benefit, ['all', 'cart', 'shipping', 'gift'], true)) {
            $benefit = 'all';
        }

        if (!\in_array($status, ['all', 'published', 'unpublished'], true)) {
            $status = 'all';
        }

        if (!\in_array($origin, ['all', 'abandoned_cart', 'other'], true)) {
            $origin = 'all';
        }

        return [
            'search' => $search,
            'search_input' => $search ?? '',
            'application' => $application,
            'assignment' => $assignment,
            'benefit' => $benefit,
            'status' => $status,
            'origin' => $origin,
            'query_parameters' => array_filter([
                'search' => $search,
                'application' => 'all' !== $application ? $application : null,
                'assignment' => 'all' !== $assignment ? $assignment : null,
                'benefit' => 'all' !== $benefit ? $benefit : null,
                'status' => 'all' !== $status ? $status : null,
                'origin' => 'all' !== $origin ? $origin : null,
            ]),
        ];
    }

    private static function trimToNull(mixed $value): ?string
    {
        if (!\is_string($value)) {
            return null;
        }

        $value = trim($value);

        return '' === $value ? null : $value;
    }
}
