<?php

declare(strict_types=1);

/*
 * 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 Serenity\CoreBundle\Shared\Twig\Breadcrumb;

use Doctrine\ORM\EntityManagerInterface;
use Knp\Menu\ItemInterface;
use Knp\Menu\Provider\MenuProviderInterface;
use Knp\Menu\Twig\Helper as MenuHelper;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RequestStack;

final readonly class BreadcrumbBuilder
{
    public function __construct(
        private MenuProviderInterface $menuProvider,
        #[Autowire(service: 'knp_menu.helper')]
        private MenuHelper $menuHelper,
        private RequestStack $requestStack,
        private EntityManagerInterface $entityManager,
    ) {
    }

    /**
     * @return array<int, array{label: string, uri: string|null, entity?: object}>
     */
    public function build(string $menuName = 'sidebarAdminMenu'): array
    {
        $request = $this->requestStack->getCurrentRequest();
        if (null === $request) {
            return [];
        }

        $route = (string) $request->attributes->get('_route');
        $params = $request->attributes->get('_route_params', []);
        $entityClass = $request->attributes->get('entity');

        $baseRoute = preg_replace('/_(edit|new|show)$/', '', $route) ?? $route;
        $menu = $this->menuProvider->get($menuName);
        $baseItem = $this->findItemByRoute($menu, $baseRoute);
        $breadcrumbs = $baseItem ? $this->menuHelper->getBreadcrumbsArray($baseItem) : [];

        if ($entityClass) {
            $entity = null;
            if (isset($params['id'])) {
                $entity = $this->entityManager->getRepository($entityClass)->find($params['id']);
            }

            $breadcrumbs = [
                ...$breadcrumbs,
                ...$this->contextualTrail($route, $entity),
            ];
        }

        if ([] !== $breadcrumbs && 'root' === mb_strtolower((string) $breadcrumbs[0]['label'])) {
            array_shift($breadcrumbs);
        }

        return $breadcrumbs;
    }

    /**
     * @return array<int, array{label: string, uri: string|null, entity?: object}>
     */
    private function contextualTrail(string $route, ?object $entity): array
    {
        /** @var array<int, array{label: string, uri: string|null, entity?: object}> $trail */
        $trail = [];

        if (str_ends_with($route, '_new')) {
            $trail[] = [
                'label' => 'Nouveau',
                'uri' => null,
            ];
        }

        if (str_ends_with($route, '_edit') && null !== $entity) {
            $trail[] = [
                'label' => \sprintf('Édition de %s', $this->entityLabel($entity)),
                'uri' => null,
                'entity' => $entity,
            ];
        }

        if (str_ends_with($route, '_show') && null !== $entity) {
            $trail[] = [
                'label' => \sprintf('Détails de %s', $this->entityLabel($entity)),
                'uri' => null,
                'entity' => $entity,
            ];
        }

        return $trail;
    }

    private function entityLabel(object $entity): string
    {
        if (method_exists($entity, '__toString')) {
            return (string) $entity;
        }

        if (method_exists($entity, 'getId')) {
            return (string) $entity->getId();
        }

        return '—';
    }

    private function findItemByRoute(ItemInterface $menu, string $route): ?ItemInterface
    {
        $routeDefinitions = $menu->getExtra('routes');
        if (is_iterable($routeDefinitions)) {
            foreach ($routeDefinitions as $definition) {
                if (($definition['route'] ?? null) === $route) {
                    return $menu;
                }
            }
        }

        foreach ($menu->getChildren() as $child) {
            $found = $this->findItemByRoute($child, $route);
            if (null !== $found) {
                return $found;
            }
        }

        return null;
    }
}
