<?php

declare(strict_types=1);

namespace core\handler;

use ReflectionClass;
use ReflectionException;
use utils\CommandInterface;
use utils\exception\FieldNotFoundInClassException;

final class SetInstanceFieldHandler extends AbstractHandler
{
    private const REQUIRED_ARGUMENTS_COUNT = 3;

    public function process(CommandInterface $command): object
    {
        $this->validate($command, self::REQUIRED_ARGUMENTS_COUNT, self::class);

        $objectInstance = $command->getPayloadByIndex(0);
        $fieldName = (string) $command->getPayloadByIndex(1);
        $newValue = $command->getPayloadByIndex(2);

        try {
            $reflectionClass = new ReflectionClass($objectInstance);
            $property = $reflectionClass->getProperty($fieldName);
            $property->setAccessible(true);
            $property->setValue($objectInstance, $newValue);
        } catch (ReflectionException $e) {
            $reflectionClass = new ReflectionClass($objectInstance);
            $properties = $reflectionClass->getProperties();
            $methods = $reflectionClass->getMethods();
            
            $valueType = gettype($newValue);
            $className = get_class($objectInstance);
            $message = "Failed to set value of type {$valueType} for {$fieldName} in class {$className}.\nAvailable public instance properties:\n";
            
            foreach ($properties as $property) {
                $type = $property->getType() ? $property->getType()->getName() : 'mixed';
                $message .= "{$type} {$property->getName()}\n";
            }
            
            $message .= "Available public instance methods:\n";
            foreach ($methods as $method) {
                $message .= "{$method->getName()}\n";
            }
            
            $message .= "Root error message: {$e->getMessage()}";
            throw new FieldNotFoundInClassException($fieldName, $objectInstance, $message);
        }

        return $objectInstance;
    }
}
