<?php

namespace AutoAdmin\Bundle\Content\Breadcrumbs\Generator;

use AutoAdmin\Bundle\Content\Breadcrumbs\Manager\BreadcrumbsGeneratorManagerInterface;
use AutoAdmin\Bundle\Content\Breadcrumbs\Model\Attributes\BreadcrumbsHierarchy;
use AutoAdmin\Bundle\Content\Breadcrumbs\Model\BreadcrumbsConfigInterface;
use AutoAdmin\Bundle\Content\Breadcrumbs\Model\BreadcrumbsConfigTranslationInterface;
use AutoAdmin\Bundle\Content\Breadcrumbs\Model\BreadcrumbsItem;
use AutoAdmin\Bundle\Content\ContentReference\ContentReferenceResolver;
use AutoAdmin\Bundle\Content\Entity\ContentReference;
use AutoAdmin\Bundle\Content\Model\NameInterface;
use AutoAdmin\Bundle\Content\Model\TranslatableInterface;
use AutoAdmin\Bundle\Content\Repository\AbstractContentRepository;
use AutoAdmin\Bundle\Content\Routing\ContentRouter;
use AutoAdmin\Bundle\Core\Helper\DoctrineHelper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Contracts\Service\Attribute\Required;

abstract class AbstractBreadcrumbsGenerator implements BreadcrumbsGeneratorInterface
{
    protected ContentRouter $contentRouter;
    protected EntityManagerInterface $em;
    protected PropertyAccessor $pa;
    protected ContentReferenceResolver $resolver;

    public function __construct()
    {
        $this->pa = new PropertyAccessor();
    }

    #[Required]
    public function setContentRouter(ContentRouter $contentRouter): void
    {
        $this->contentRouter = $contentRouter;
    }

    #[Required]
    public function setEntityManager(EntityManagerInterface $em): void
    {
        $this->em = $em;
    }

    public function generateFragments(?object $subject, BreadcrumbsGeneratorManagerInterface $manager): array
    {
        $fragments = [];
        $fragments[] = $this->getBreadcrumbsItem($subject);
        $fragments = $this->handleParentPath($subject, $fragments, $manager);
        $fragments = $this->handleBridgedContent($subject, $fragments, $manager);

        return $fragments;
    }

    protected function getAttributeInstance(object $subject): ?BreadcrumbsHierarchy
    {
        $refl = new \ReflectionClass(DoctrineHelper::getClass($subject));
        $bcAttribute = $refl->getAttributes(BreadcrumbsHierarchy::class)[0] ?? null;

        if ($bcAttribute && $attr = $bcAttribute->newInstance()) {
            return $attr;
        }

        return null;
    }

    protected function handleParentPath(
        object $subject,
        array $fragments,
        BreadcrumbsGeneratorManagerInterface $manager
    ) {
        $attr = $this->getAttributeInstance($subject);
        if ($attr && $path = $attr->parentPath) {
            $parentAttributeObject = $this->pa->getValue($subject, $path);

            // content ref for external hierarchy
            if ($parentAttributeObject instanceof ContentReference) {
                $parentAttributeObject = $this->resolver->resolve($parentAttributeObject);
            }

            if ($parentAttributeObject) {
                $fragments = [...$fragments, ...$manager->generate($parentAttributeObject)];
            }
        }

        return $fragments;
    }

    protected function handleBridgedContent(
        object $subject,
        array $fragments,
        BreadcrumbsGeneratorManagerInterface $manager
    ) {
        $attr = $this->getAttributeInstance($subject);

        if ($attr && ($class = $attr->bridgeToContent) && ($slugAdmin = $attr->bridgeToSlugAdmin)) {
            $content = $this->getBridgeContent($class, $slugAdmin);
            if ($content) {
                $fragments = [...$fragments, ...$manager->generate($content)];
            }
        }

        return $fragments;
    }

    protected function getBridgeContent(string $contentType, string $slugAdmin)
    {
        /** @var AbstractContentRepository $repo */
        $contentType = DoctrineHelper::removeProxyPrefix($contentType);
        $repo = $this->em->getRepository($contentType);
        $content = $repo->contentFindOneBy(['_.slugAdmin' => $slugAdmin]);

        return $content;
    }

    protected function getBreadcrumbsItem(object $subject): BreadcrumbsItem
    {
        $item = new BreadcrumbsItem($subject);

        // override label
        if (
            $subject instanceof BreadcrumbsConfigInterface
            && $subject instanceof TranslatableInterface
            && ($tr = $subject->getCurrentTranslation())
            && $tr instanceof BreadcrumbsConfigTranslationInterface
            && $label = $tr->getBreadcrumbsLabel()
        ) {
            $item->setLabel($label);
        }

        // fallback on deduced "name" label
        if ($item->getLabel() === null
            && $subject instanceof TranslatableInterface
            && ($tr = $subject->getCurrentTranslation())
            && $tr instanceof NameInterface
            && $name = trim($tr->getName())
        ) {
            $item->setLabel($name);
        }

        return $item;
    }
}
