<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\ArgumentResolver;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;

/**
 * Resolves entity arguments for controllers.
 *
 * This replaces the functionality previously provided by SensioFrameworkExtraBundle.
 */
class EntityValueResolver implements ValueResolverInterface
{
    public function __construct(
        private readonly ManagerRegistry $doctrine,
    ) {}

    public function resolve(Request $request, ArgumentMetadata $argument): iterable
    {
        $type = $argument->getType();
        if (null === $type || !class_exists($type) || !$this->isEntity($type)) {
            return [];
        }

        $name = $argument->getName();

        // If the argument name is 'id', we don't want to resolve it as an entity
        if ('id' === $name) {
            return [];
        }

        // Try to find the entity ID from various possible parameter names
        $id = $this->findEntityId($request, $name, $type);

        if (null === $id) {
            return [];
        }

        $entity = $this->doctrine->getRepository($type)->find($id);

        if (null === $entity) {
            return [];
        }

        return [$entity];
    }

    /**
     * Tries to find the entity ID from various possible parameter names.
     *
     * This method checks for the following parameter names in order:
     * 1. The parameter name specified in the route (e.g., 'id', 'person_id', 'household_id')
     * 2. A parameter named after the entity with 'Id' suffix (e.g., 'personId' for Person entity)
     * 3. A parameter named after the entity with '_id' suffix (e.g., 'person_id' for Person entity)
     * 4. The 'id' parameter as a fallback
     */
    private function findEntityId(Request $request, string $name, string $type): mixed
    {
        // Common parameter naming patterns
        $possibleNames = [
            $name . 'Id',
            $name . '_id',
            $this->getShortClassName($type) . '_id',
            $this->getShortClassName($type) . 'Id',
            'id',
        ];

        // Special cases based on observed @ParamConverter usage
        $specialCases = [
            'Household' => ['household_id'],
            'AccompanyingCourse' => ['accompanying_period_id'],
            'AccompanyingPeriod' => ['accompanying_period_id'],
            'Event' => ['event_id'],
            'User' => ['userId'],
            'Person' => ['person_id'],
            'Action' => ['action_id'],
        ];

        // Add special cases if applicable
        $shortClassName = $this->getShortClassName($type);
        if (isset($specialCases[$shortClassName])) {
            $possibleNames = array_merge($specialCases[$shortClassName], $possibleNames);
        }

        // Try each possible parameter name
        foreach ($possibleNames as $paramName) {
            if ($request->attributes->has($paramName)) {
                return $request->attributes->get($paramName);
            }
        }

        return null;
    }

    /**
     * Gets the short class name (without namespace) from a fully qualified class name.
     */
    private function getShortClassName(string $class): string
    {
        $parts = explode('\\', $class);
        return end($parts);
    }

    private function isEntity(string $class): bool
    {
        if (!$this->doctrine->getManagerForClass($class) instanceof EntityManagerInterface) {
            return false;
        }

        return !$this->doctrine->getManagerForClass($class)->getMetadataFactory()->isTransient($class);
    }
}
