<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace GuardDog\Vendor\Symfony\Component\Serializer\Mapping\Loader;

use GuardDog\Vendor\Symfony\Component\Serializer\Attribute\Ignore;
/**
 * Provides methods to detect accessor name collisions during serialization.
 *
 * @internal
 */
trait AccessorCollisionResolverTrait
{
    private function getAttributeNameFromAccessor(\ReflectionClass $class, \ReflectionMethod $method, bool $andMutator): ?string
    {
        $methodName = $method->name;
        $i = match ($methodName[0]) {
            's' => $andMutator && str_starts_with($methodName, 'set') ? 3 : null,
            'g' => str_starts_with($methodName, 'get') ? 3 : null,
            'h' => str_starts_with($methodName, 'has') ? 3 : null,
            'c' => str_starts_with($methodName, 'can') ? 3 : null,
            'i' => str_starts_with($methodName, 'is') ? 2 : null,
            default => null,
        };
        // ctype_lower check to find out if method looks like accessor but actually is not, e.g. hash, cancel
        if (null === $i || ctype_lower($methodName[$i] ?? 'a') || $method->isStatic()) {
            return null;
        }
        if ('s' === $methodName[0] ? !$method->getNumberOfParameters() : $method->getNumberOfRequiredParameters() || \in_array((string) $method->getReturnType(), ['void', 'never'], \true)) {
            return null;
        }
        $attributeName = substr($methodName, $i);
        if (!$class->hasProperty($attributeName)) {
            $attributeName = lcfirst($attributeName);
        }
        return $attributeName;
    }
    private function hasPropertyForAccessor(\ReflectionClass $class, string $propName): bool
    {
        do {
            if ($class->hasProperty($propName)) {
                return \true;
            }
        } while ($class = $class->getParentClass());
        return \false;
    }
    private function hasAttributeNameCollision(\ReflectionClass $class, string $attributeName, string $methodName): bool
    {
        if ($this->hasPropertyForAccessor($class, $attributeName)) {
            return \true;
        }
        if ($class->hasMethod($attributeName)) {
            $candidate = $class->getMethod($attributeName);
            if ($candidate->getName() !== $methodName && $this->isReadableAccessorMethod($candidate)) {
                return \true;
            }
        }
        $ucAttributeName = ucfirst($attributeName);
        foreach (['get', 'is', 'has', 'can'] as $prefix) {
            $candidateName = $prefix . $ucAttributeName;
            if ($candidateName === $methodName || !$class->hasMethod($candidateName)) {
                continue;
            }
            if ($this->isReadableAccessorMethod($class->getMethod($candidateName))) {
                return \true;
            }
        }
        return \false;
    }
    private function isReadableAccessorMethod(\ReflectionMethod $method): bool
    {
        return $method->isPublic() && !$method->isStatic() && !$method->getAttributes(Ignore::class) && !$method->getNumberOfRequiredParameters() && !\in_array((string) $method->getReturnType(), ['void', 'never'], \true);
    }
}
