<?php
namespace Shared;

use App\Router\Request\IRequest;
/**
 * Responsible for resolving the arguments passed to a controller action.
 */
class ArgumentControllerResolver implements IArgumentResolver{

/**
     * Resolves and returns the arguments for a given controller callable.
     *
     * @param IRequest $request    The HTTP request object.
     * @param callable $controller The controller callable.
     * @return array               An array of resolved arguments.
     * @throws \ReflectionException If the controller method does not exist.
     * @throws \InvalidArgumentException If an argument cannot be resolved.
     */
    public function getArguments(IRequest $request, callable $controller): array
    {
        try {
            
            // Check if $controller is an array and has two elements (class and method)
            if (is_array($controller) && count($controller) === 2) {
                $className = is_object($controller[0]) ? get_class($controller[0]) : $controller[0];
                $methodName = $controller[1];
                $reflectionMethod = new \ReflectionMethod($className, $methodName);
            } else {
                // Handle other types of callables if needed
                throw new \InvalidArgumentException("Invalid controller callable format.");
            }
        } catch (\ReflectionException $e) {
            throw new \InvalidArgumentException("Controller method error: " . $e->getMessage());
        }

        $args = [];
        foreach ($reflectionMethod->getParameters() as $param) {
            if (IRequest::class === $param->getType()->getName() || is_subclass_of($param->getType()->getName(), IRequest::class)) {
                $args[] = $request;
                continue;
            }

            $name = $param->getName();
            $value = $this->getFromRequest($name, $request);

            if ($value === null && $param->isDefaultValueAvailable()) {
                $value = $param->getDefaultValue();
            } elseif ($value === null) {
                throw new \InvalidArgumentException("Missing argument: $name");
            }

            $args[] = $value;
        }

        return $args;
    }

  /**
     * Extracts a value from the request based on a key.
     *
     * @param string $key The key to look for in the request.
     * @param IRequest $req The request object.
     * @return mixed The value from the request or null if not found.
     */
    public function getFromRequest(string $key, IRequest $req): mixed
    {
        $body = $req->getBody();
        if (array_key_exists($key, $body)) {
            return $body[$key];
        }

        $queryParams = $req->getQueryParameters();
        if (array_key_exists($key, $queryParams)) {
            return $queryParams[$key];
        }

        $requestParams = $req->getRequestParameters();
        if (array_key_exists($key, $requestParams)) {
            return $requestParams[$key];
        }

        return null;
    }
}

