<?php

/*
 * This file is part of the Serenity 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\Admin;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Admin\ProductFormType;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Repository\ProductCategoryRepository;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use CpCreation\VitiCore\Product\Repository\ProductRepository;
use CpCreation\VitiCore\Product\Repository\ProductTypeRepository;
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('/produits')]
#[IsGranted(new Expression("is_granted('ROLE_ADMIN') or is_granted('ROLE_PRODUCT') or is_granted('ROLE_PRODUCT_PRICE')"))]
final class ProductController extends AbstractController
{
    public function __construct(
        private readonly ProductRepository $productRepository,
        private readonly ProductCategoryRepository $productCategoryRepository,
        private readonly ProductTypeRepository $productTypeRepository,
        private readonly ProductPriceGroupRepository $productPriceGroupRepository,
    ) {
    }

    #[Route('', name: 'admin_product')]
    public function index(Request $request): Response
    {
        $filters = $this->extractFilters($request);
        $products = $this->productRepository->findByAdminFilters($filters);
        $productsByPosition = $this->productRepository->findByPositionOrderAdmin();
        $productsByPositionShop = $this->productRepository->findByPositionShopOrderAdmin();

        return $this->render('@CpCreationVitiCore/admin/product/index.html.twig', [
            'items' => $products,
            'categories' => $this->productCategoryRepository->findByPositionOrderAdmin(),
            'variants' => $this->productTypeRepository->findByPositionOrderAdmin(),
            'groups' => $this->productPriceGroupRepository->findByPositionOrderAdmin(),
            'filters' => $filters,
            'filterQuery' => $this->buildFilterQuery($filters),
            'maxPosition' => $this->findMaxPosition($productsByPosition, 'getPosition'),
            'maxPositionShop' => $this->findMaxPosition($productsByPositionShop, 'getPositionShop'),
        ]);
    }

    #[Route(
        '/new',
        name: 'admin_product_new'
    )]
    #[IsGranted(new Expression("is_granted('ROLE_ADMIN')"))]
    public function new(Request $request): Response
    {
        $product = new Product();
        $form = $this->createForm(ProductFormType::class, $product);
        $form->handleRequest($request);

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

            $this->productRepository->save($data);
            $this->addFlash('alert-success', 'Le produit '.$data->getName().' a été ajouté avec succès');

            return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
        }

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

    #[Route('/{id}/edit', name: 'admin_product_edit')]
    public function edit(Product $product, Request $request): Response
    {
        $form = $this->createForm(ProductFormType::class, $product);
        $form->handleRequest($request);

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

            $data->setUpdatedAt(new \DateTimeImmutable()); // Forcer Update de doctrine
            $this->productRepository->save($data);

            $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été modifié avec succès');

            return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
        }

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

    #[Route('/{id}/delete', name: 'admin_product_delete')]
    public function delete(Product $product, Request $request): Response
    {
        $this->productRepository->softDelete($product);
        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été supprimé avec succès.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/{id}/disable', name: 'admin_product_disable')]
    public function disable(Product $product, Request $request): Response
    {
        $product->setDisabled(1);
        $this->productRepository->save($product);

        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été dépublié.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/{id}/enable', name: 'admin_product_enable')]
    public function enable(Product $product, Request $request): Response
    {
        $product->setDisabled(0);
        $this->productRepository->save($product);

        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été publié.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/{id}/moveUp', name: 'admin_product_move_up')]
    public function moveUp(Product $product, Request $request): Response
    {
        if (($product->getPosition() - 1) < 0) {
            $this->addFlash('alert-warning', 'Le produit '.$product->getName().' ne peut être remonté.');

            return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
        }

        $product->setPosition($product->getPosition() - 1);
        $this->productRepository->save($product);

        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été remonté.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/{id}/moveDown', name: 'admin_product_move_down')]
    public function moveDown(Product $product, Request $request): Response
    {
        $product->setPosition($product->getPosition() + 1);
        $this->productRepository->save($product);

        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été descendu.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/{id}/moveUpShop', name: 'admin_product_move_up_shop')]
    public function moveUpShop(Product $product, Request $request): Response
    {
        if (($product->getPositionShop() - 1) < 0) {
            $this->addFlash('alert-warning', 'Le produit '.$product->getName().' ne peut être remonté.');

            return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
        }

        $products = $this->productRepository->findByPositionShopOrderAdmin();
        $targetPos = $product->getPositionShop() - 1;

        foreach ($products as $item) {
            if ($item->getPositionShop() === $targetPos) {
                $item->setPositionShop($product->getPositionShop());
                $this->productRepository->save($item);
                break;
            }
        }

        $product->setPositionShop($targetPos);
        $this->productRepository->save($product);

        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été remonté.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/{id}/moveDownShop', name: 'admin_product_move_down_shop')]
    public function moveDownShop(Product $product, Request $request): Response
    {
        $products = $this->productRepository->findByPositionShopOrderAdmin();
        $targetPos = $product->getPositionShop() + 1;

        foreach ($products as $item) {
            if ($item->getPositionShop() === $targetPos) {
                $item->setPositionShop($product->getPositionShop());
                $this->productRepository->save($item);
                break;
            }
        }

        $product->setPositionShop($targetPos);
        $this->productRepository->save($product);

        $this->addFlash('alert-success', 'Le produit '.$product->getName().' a été descendu.');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/initPositionShop', name: 'admin_product_init_position_shop')]
    public function initPositionShop(Request $request): Response
    {
        $products = $this->productRepository->findByPositionOrderAdmin();
        $i = 0;

        foreach ($products as $product) {
            if ($product->getDeletedAt()) {
                $product->setPositionShop(-1);
                --$i;
            } else {
                $product->setPositionShop($i);
            }

            $this->productRepository->persist($product);
            ++$i;
        }

        $this->productRepository->flush();
        $this->addFlash('alert-success', 'La position des produits dans la page boutique a été réinitialisée');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    #[Route('/initPosition', name: 'admin_product_init_position')]
    public function initPosition(Request $request): Response
    {
        $products = $this->productRepository->findByCreatedAdminAt('ASC');
        $i = 0;

        foreach ($products as $product) {
            if ($product->getDeletedAt()) {
                $product->setPosition(-1);
                --$i;
            } else {
                $product->setPosition($i);
            }

            $this->productRepository->save($product);
            ++$i;
        }

        $this->productRepository->flush();
        $this->addFlash('alert-success', 'La position des produits dans la page cuvées a été réinitialisée');

        return $this->redirectToRoute('admin_product', $this->extractReturnQuery($request));
    }

    /**
     * @return array{
     *     category: string|null,
     *     variant: string|null,
     *     group: string|null,
     *     cuvee: bool|null,
     *     shop: bool|null
     * }
     */
    private function extractFilters(Request $request): array
    {
        return [
            'category' => $this->normalizeString($request->query->get('category')),
            'variant' => $this->normalizeString($request->query->get('variant')),
            'group' => $this->normalizeString($request->query->get('group')),
            'cuvee' => $this->normalizeBoolean($request->query->get('cuvee')),
            'shop' => $this->normalizeBoolean($request->query->get('shop')),
        ];
    }

    /**
     * @param array{
     *     category: string|null,
     *     variant: string|null,
     *     group: string|null,
     *     cuvee: bool|null,
     *     shop: bool|null
     * } $filters
     *
     * @return array<string, string>
     */
    private function buildFilterQuery(array $filters): array
    {
        $query = [];

        foreach ($filters as $key => $value) {
            if (null === $value) {
                continue;
            }

            $query[$key] = \is_bool($value) ? ($value ? '1' : '0') : $value;
        }

        return $query;
    }

    /**
     * @return array<string, string>
     */
    private function extractReturnQuery(Request $request): array
    {
        return $this->buildFilterQuery($this->extractFilters($request));
    }

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

        $value = trim($value);

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

    private function normalizeBoolean(mixed $value): ?bool
    {
        if ('1' === $value || 1 === $value || true === $value) {
            return true;
        }

        if ('0' === $value || 0 === $value || false === $value) {
            return false;
        }

        return null;
    }

    /**
     * @param Product[] $products
     */
    private function findMaxPosition(array $products, string $method): ?int
    {
        $maxPosition = null;

        foreach ($products as $product) {
            $position = $product->{$method}();

            if (null === $position) {
                continue;
            }

            if (null === $maxPosition || $position > $maxPosition) {
                $maxPosition = $position;
            }
        }

        return $maxPosition;
    }
}
