<?php

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

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Admin\ShippingPriceType;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Model\ProductType;
use CpCreation\VitiCore\Shop\Model\ShippingPrice;
use CpCreation\VitiCore\Shop\Model\ShippingZone;
use CpCreation\VitiCore\Shop\Model\DeliveryMethod;
use CpCreation\VitiCore\Shop\Repository\ShippingPriceRepository;
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('/shipping-price')]
#[IsGranted(new Expression("is_granted('ROLE_ADMIN') or is_granted('ROLE_SHIPPING')"))]
final class ShippingPriceController extends AbstractController
{
    public function __construct(
        private readonly ShippingPriceRepository $shippingPriceRepository,
    ) {}

    #[Route('', name: 'admin_shipping_price')]
    public function index(Request $request): Response
    {
        $prices = $this->shippingPriceRepository->findOrder();
        $filterOptions = $this->buildFilterOptions($prices);
        $filters = $this->buildFilters($request, $filterOptions);
        $items = array_values(array_filter(
            $prices,
            fn (ShippingPrice $price): bool => $this->matchesFilters($price, $filters)
        ));

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

    #[Route('/new', name: 'admin_shipping_price_new')]
    public function new(Request $request): Response
    {
        $queryParameters = $this->buildListQueryParameters($request);
        $form = $this->createForm(ShippingPriceType::class);
        $form->handleRequest($request);

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

            $this->shippingPriceRepository->save($data);
            $this->addFlash('alert-success', 'Le prix a été ajouté avec succès');

            return $this->redirectToRoute('admin_shipping_price', $queryParameters);
        }

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

    #[Route('/{id}/edit', name: 'admin_shipping_price_edit')]
    public function edit(ShippingPrice $price, Request $request): Response
    {
        $queryParameters = $this->buildListQueryParameters($request);
        $form = $this->createForm(ShippingPriceType::class, $price);
        $form->handleRequest($request);

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

            $this->shippingPriceRepository->save($data);
            $this->addFlash('alert-success', 'Le prix a été modifié avec succès');

            return $this->redirectToRoute('admin_shipping_price', $queryParameters);
        }

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

    #[Route('/{id}/delete', name: 'admin_shipping_price_delete')]
    public function delete(ShippingPrice $price, Request $request): Response
    {
        $queryParameters = $this->buildListQueryParameters($request);
        $this->shippingPriceRepository->delete($price);
        $this->addFlash('alert-success', 'Le prix a été supprimé avec succès.');

        return $this->redirectToRoute('admin_shipping_price', $queryParameters);
    }

    /**
     * @param list<ShippingPrice> $prices
     *
     * @return array{
     *     groups: array<string, string>,
     *     carriers: array<string, string>,
     *     types: array<string, string>,
     *     zones: array<string, string>,
     *     delivery_methods: array<string, string>
     * }
     */
    private function buildFilterOptions(array $prices): array
    {
        $groups = [];
        $carriers = [];
        $types = [];
        $zones = [];
        $deliveryMethods = [];

        foreach ($prices as $price) {
            $carrier = self::trimToNull($price->getNameCarrier());
            if (null !== $carrier) {
                $carriers[$carrier] = $carrier;
            }

            foreach ($price->getGroups() as $group) {
                if (!$group instanceof ProductPriceGroup) {
                    continue;
                }

                $groups[$group->getId()->toString()] = $group->getName() ?? 'Sans nom';
            }

            foreach ($price->getTypes() as $type) {
                if (!$type instanceof ProductType) {
                    continue;
                }

                $types[$type->getId()->toString()] = $this->formatProductTypeLabel($type);
            }

            foreach ($price->getZones() as $zone) {
                if (!$zone instanceof ShippingZone) {
                    continue;
                }

                $zones[$zone->getId()->toString()] = $zone->getName() ?? 'Sans nom';
            }

            $deliveryMethod = $price->getDeliveryMethod();
            if ($deliveryMethod instanceof DeliveryMethod) {
                $deliveryMethods[$deliveryMethod->getId()->toString()] = $deliveryMethod->getName() ?? 'Sans nom';
            }
        }

        asort($groups, \SORT_NATURAL | \SORT_FLAG_CASE);
        asort($carriers, \SORT_NATURAL | \SORT_FLAG_CASE);
        asort($types, \SORT_NATURAL | \SORT_FLAG_CASE);
        asort($zones, \SORT_NATURAL | \SORT_FLAG_CASE);
        asort($deliveryMethods, \SORT_NATURAL | \SORT_FLAG_CASE);

        return [
            'groups' => $groups,
            'carriers' => $carriers,
            'types' => $types,
            'zones' => $zones,
            'delivery_methods' => $deliveryMethods,
        ];
    }

    /**
     * @param array{
     *     groups: array<string, string>,
     *     carriers: array<string, string>,
     *     types: array<string, string>,
     *     zones: array<string, string>,
     *     delivery_methods: array<string, string>
     * } $filterOptions
     *
     * @return array{
     *     group_id: ?string,
     *     carrier: ?string,
     *     type_id: ?string,
     *     zone_id: ?string,
     *     delivery_method_id: ?string,
     *     query_parameters: array<string, string>
     * }
     */
    private function buildFilters(Request $request, array $filterOptions): array
    {
        $groupId = self::trimToNull($request->query->get('group_id'));
        $carrier = self::trimToNull($request->query->get('carrier'));
        $typeId = self::trimToNull($request->query->get('type_id'));
        $zoneId = self::trimToNull($request->query->get('zone_id'));
        $deliveryMethodId = self::trimToNull($request->query->get('delivery_method_id'));

        if (null !== $groupId && !array_key_exists($groupId, $filterOptions['groups'])) {
            $groupId = null;
        }

        if (null !== $carrier && !array_key_exists($carrier, $filterOptions['carriers'])) {
            $carrier = null;
        }

        if (null !== $typeId && !array_key_exists($typeId, $filterOptions['types'])) {
            $typeId = null;
        }

        if (null !== $zoneId && !array_key_exists($zoneId, $filterOptions['zones'])) {
            $zoneId = null;
        }

        if (
            null !== $deliveryMethodId
            && '__none__' !== $deliveryMethodId
            && !array_key_exists($deliveryMethodId, $filterOptions['delivery_methods'])
        ) {
            $deliveryMethodId = null;
        }

        return [
            'group_id' => $groupId,
            'carrier' => $carrier,
            'type_id' => $typeId,
            'zone_id' => $zoneId,
            'delivery_method_id' => $deliveryMethodId,
            'query_parameters' => array_filter([
                'group_id' => $groupId,
                'carrier' => $carrier,
                'type_id' => $typeId,
                'zone_id' => $zoneId,
                'delivery_method_id' => $deliveryMethodId,
            ], static fn (?string $value): bool => null !== $value && '' !== $value),
        ];
    }

    /**
     * @param array{
     *     group_id: ?string,
     *     carrier: ?string,
     *     type_id: ?string,
     *     zone_id: ?string,
     *     delivery_method_id: ?string,
     *     query_parameters: array<string, string>
     * } $filters
     */
    private function matchesFilters(ShippingPrice $price, array $filters): bool
    {
        if (null !== $filters['group_id'] && !$this->collectionContainsId($price->getGroups(), $filters['group_id'])) {
            return false;
        }

        if (null !== $filters['carrier'] && self::trimToNull($price->getNameCarrier()) !== $filters['carrier']) {
            return false;
        }

        if (null !== $filters['type_id'] && !$this->collectionContainsId($price->getTypes(), $filters['type_id'])) {
            return false;
        }

        if (null !== $filters['zone_id'] && !$this->collectionContainsId($price->getZones(), $filters['zone_id'])) {
            return false;
        }

        if ('__none__' === $filters['delivery_method_id']) {
            return null === $price->getDeliveryMethod();
        }

        if (
            null !== $filters['delivery_method_id']
            && $price->getDeliveryMethod()?->getId()->toString() !== $filters['delivery_method_id']
        ) {
            return false;
        }

        return true;
    }

    private function collectionContainsId(iterable $items, string $id): bool
    {
        foreach ($items as $item) {
            if (!\is_object($item) || !method_exists($item, 'getId')) {
                continue;
            }

            if ($item->getId()->toString() === $id) {
                return true;
            }
        }

        return false;
    }

    private function formatProductTypeLabel(ProductType $type): string
    {
        $label = $type->getName() ?? 'Sans nom';
        $info = self::trimToNull($type->getInfo());

        if (null !== $info) {
            return sprintf('%s (%s)', $label, $info);
        }

        return $label;
    }

    /**
     * @return array<string, string>
     */
    private function buildListQueryParameters(Request $request): array
    {
        return array_filter([
            'group_id' => self::trimToNull($request->query->get('group_id')),
            'carrier' => self::trimToNull($request->query->get('carrier')),
            'type_id' => self::trimToNull($request->query->get('type_id')),
            'zone_id' => self::trimToNull($request->query->get('zone_id')),
            'delivery_method_id' => self::trimToNull($request->query->get('delivery_method_id')),
        ], static fn (?string $value): bool => null !== $value && '' !== $value);
    }

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

        $value = trim($value);

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