<?php


namespace AutoAdmin\Bundle\Content\Doctrine\Repository;


use AutoAdmin\Bundle\Content\Model\UnstranslatedInterface;
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\Persistence\ObjectRepository;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use RuntimeException;

/**
 * Class ContainerRepositoryFactory
 *
 * inspired by \Doctrine\Bundle\DoctrineBundle\Repository\ContainerRepositoryFactory
 * altered to search for auto-defined repositories
 *
 * @package AutoAdmin\Bundle\Content\Doctrine\Repository
 */
class ContainerRepositoryFactory implements RepositoryFactory
{
    /** @var ObjectRepository[] */
    private array $managedRepositories = [];

    private ?ContainerInterface $container = null;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * {@inheritdoc}
     */
    public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository
    {
        // try to find if already-created default repository exists
        $metadata = $entityManager->getClassMetadata($entityName);
        $refl = new ReflectionClass($entityName);
        $idGenerator = new RepositoryServiceIdGenerator();
        $serviceId = $idGenerator->generate($entityName); // TODO remove broken repo-generated ids ??? => yes, this shit is breaking everything !!!!!
        $serviceId = $this->getRepoClassFromEntity($entityName);
        if (
            $refl->isSubclassOf(UnstranslatedInterface::class)
            && $this->container->has($serviceId)
        ) {
            return $this->container->get($serviceId);
        }

//        if(stripos($entityName, 'cocoon') !== false)
//        {
//            dump($serviceId);
//            dump($this->container->get($serviceId));
//            die('failed');
//        }

        // if nothing was found, default lookup
        $customRepositoryName = $repositoryServiceId = $metadata->customRepositoryClassName;
        if ($customRepositoryName !== null) {
            // fetch from the container
            if ($this->container && $this->container->has($customRepositoryName)) {
                $repository = $this->container->get($customRepositoryName);
//                 dump($repository);
                if (!$repository instanceof ObjectRepository) {
//                    die;
                    throw new RuntimeException(
                        sprintf(
                            'The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository).',
                            $repositoryServiceId
                        )
                    );
                }

                return $repository;
            }

            // if not in the container but the class/id implements the interface, throw an error
            if (is_a($customRepositoryName, ServiceEntityRepositoryInterface::class, true)) {
                throw new RuntimeException(
                    sprintf(
                        'The "%s" entity repository implements "%s", but its service could not be found. Make sure the service exists and is tagged with "%s".',
                        $customRepositoryName,
                        ServiceEntityRepositoryInterface::class,
                        ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG
                    )
                );
            }

            if (!class_exists($customRepositoryName)) {
                throw new RuntimeException(
                    sprintf(
                        'The "%s" entity has a repositoryClass set to "%s", but this is not a valid class. Check your class naming. If this is meant to be a service id, make sure this service exists and is tagged with "%s".',
                        $metadata->name,
                        $customRepositoryName,
                        ServiceRepositoryCompilerPass::REPOSITORY_SERVICE_TAG
                    )
                );
            }

            // allow the repository to be created below
        }

        return $this->getOrCreateRepository($entityManager, $metadata);
    }

    public function getRepoClassFromEntity(string $entityClass): string
    {
        $repoClass = str_replace('\\Entity\\', '\\Repository\\', $entityClass);
        $repoClass = $repoClass . 'Repository';

        return $repoClass;
    }

    private function getOrCreateRepository(EntityManagerInterface $entityManager, ClassMetadata $metadata)
    {
        $repositoryHash = $metadata->getName() . spl_object_hash($entityManager);
        if (isset($this->managedRepositories[$repositoryHash])) {
            return $this->managedRepositories[$repositoryHash];
        }

        $repositoryClassName = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();

        return $this->managedRepositories[$repositoryHash] = new $repositoryClassName($entityManager, $metadata);
    }
}
