private function transformParams(array $params)
    {
        $locations = [];
        $namespace = [];
        foreach ($params as $name => $param) {
            if (isset($param['name'])) {
                $name = $param['name'];
            } else {
                $param['name'] = $name;
            }
            $param = $this->transformSchema($param);
            $location = isset($param['location']) ? $param['location'] : '';
            // resolve models immediately. Guzzle will resolve anyway and we need the data for request body transforms
            if (isset($param['$ref'])) {
                $param = $this->models[$param['$ref']];
            }
            // handle paramType -> location mapping.
            if ($location) {
                $location = $param['location'] = $this->transformLocation($location);
                // swagger doesn't allow optional path params
                if (!isset($param['required'])) {
                    $param['required'] = 'uri' === $location;
                }
            }
            // handle serialization in request body.
            if ('body' === $location) {
                $location = $this->requestType;
                // objects properties must be moved into parent namespace or Guzzle will wrap them.
                /** @var array[] $param */
                if (isset($param['properties'])) {
                    foreach ($param['properties'] as $propertyName => $property) {
                        $property['location'] = $location;
                        $locations[$location][$propertyName] = $property;
                        $namespace[$propertyName][$location] = 1;
                    }
                    continue;
                }
            }
            // else add single parameter by name
            $locations[$location][$name] = $param;
            $namespace[$name][$location] = 1;
        }
        // resolve all locations to single namespace
        // conflict can occur due to differences in swagger/guzzle modelling of complex params
        $target = [];
        /**
         * @var string $location
         * @var array[] $parameters
         */
        foreach ($locations as $location => $parameters) {
            foreach ($parameters as $name => $param) {
                unset($namespace[$name][$location]);
                if ($conflicts = array_keys($namespace[$name])) {
                    $alias = $name.'_'.$location;
                    $this->debug('! %s parameter "%s" conflicts with %s, address as "%s"', $location, $name, implode(' and ', $conflicts), $alias);
                    // namespace this property at our end ensuring it's sent to the API as expected.
                    $param['sentAs'] = $name;
                    $name = $param['name'] = $alias;
                }
                $target[$name] = $param;
            }
        }
        return $target;
    }