<?php
/**
 * SlimRouter
 *
 * PHP version 7
 *
 * @package OpenAPIServer
 * @author  OpenAPI Generator team
 * @link    https://github.com/openapitools/openapi-generator
 */

/**
 * OpenAPI Petstore *_/ ' \" =end -- \\r\\n \\n \\r
 *
 * This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\  *_/ ' \" =end --
 * OpenAPI spec version: 1.0.0 *_/ ' \" =end -- \\r\\n \\n \\r
 * Contact: something@something.abc *_/ ' \" =end -- \\r\\n \\n \\r
 * Generated by: https://github.com/openapitools/openapi-generator.git
 */

/**
 * NOTE: This class is auto generated by the openapi generator program.
 * https://github.com/openapitools/openapi-generator
 * Do not edit the class manually.
 */
namespace OpenAPIServer;

use Slim\App;
use Slim\Interfaces\RouteInterface;
use Psr\Container\ContainerInterface;
use InvalidArgumentException;
use Dyorg\TokenAuthentication;
use Dyorg\TokenAuthentication\TokenSearch;
use Psr\Http\Message\ServerRequestInterface;
use Exception;

/**
 * SlimRouter Class Doc Comment
 *
 * @package OpenAPIServer
 * @author  OpenAPI Generator team
 * @link    https://github.com/openapitools/openapi-generator
 */
class SlimRouter
{

    /** @var App instance */
    private $slimApp;

    /** @var array[] list of all api operations */
    private $operations = [
        [
            'httpMethod' => 'PUT',
            'basePathWithoutHost' => '/%20%27%20%22%20%3Dend%20--%20%5C%5Cr%5C%5Cn%20%5C%5Cn%20%5C%5Cr/v2%20*_/%20%27%20%22%20%3Dend%20--%20%5C%5Cr%5C%5Cn%20%5C%5Cn%20%5C%5Cr',
            'path' => '/fake',
            'apiPackage' => 'OpenAPIServer\Api',
            'classname' => 'AbstractFakeApi',
            'userClassname' => 'FakeApi',
            'operationId' => 'testCodeInjectEndRnNR',
            'authMethods' => [
            ],
        ],
    ];

    /**
     * Class constructor
     *
     * @param ContainerInterface|array $container Either a ContainerInterface or an associative array of app settings
     *
     * @throws InvalidArgumentException When no container is provided that implements ContainerInterface
     * @throws Exception When implementation class doesn't exists
     */
    public function __construct($container = [])
    {
        $this->slimApp = new App($container);

        $authPackage = 'OpenAPIServer\Auth';
        $basicAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
            $message = "How about extending AbstractAuthenticator class by {$authPackage}\BasicAuthenticator?";
            throw new Exception($message);
        };
        $apiKeyAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
            $message = "How about extending AbstractAuthenticator class by {$authPackage}\ApiKeyAuthenticator?";
            throw new Exception($message);
        };
        $oAuthAuthenticator = function (ServerRequestInterface &$request, TokenSearch $tokenSearch) use ($authPackage) {
            $message = "How about extending AbstractAuthenticator class by {$authPackage}\OAuthAuthenticator?";
            throw new Exception($message);
        };

        foreach ($this->operations as $operation) {
            $callback = function ($request, $response, $arguments) use ($operation) {
                $message = "How about extending {$operation['classname']} by {$operation['apiPackage']}\\{$operation['userClassname']} class implementing {$operation['operationId']} as a {$operation['httpMethod']} method?";
                throw new Exception($message);
                return $response->withStatus(501)->write($message);
            };
            $middlewares = [];

            if (class_exists("\\{$operation['apiPackage']}\\{$operation['userClassname']}")) {
                $callback = "\\{$operation['apiPackage']}\\{$operation['userClassname']}:{$operation['operationId']}";
            }

            foreach ($operation['authMethods'] as $authMethod) {
                switch ($authMethod['type']) {
                    case 'http':
                        $authenticatorClassname = "\\{$authPackage}\\BasicAuthenticator";
                        if (class_exists($authenticatorClassname)) {
                            $basicAuthenticator = new $authenticatorClassname($container);
                        }

                        $middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
                            'authenticator' => $basicAuthenticator,
                            'regex' => '/Basic\s+(.*)$/i',
                            'header' => 'Authorization',
                            'parameter' => null,
                            'cookie' => null,
                            'argument' => null,
                        ]));
                        break;
                    case 'apiKey':
                        $authenticatorClassname = "\\{$authPackage}\\ApiKeyAuthenticator";
                        if (class_exists($authenticatorClassname)) {
                            $apiKeyAuthenticator = new $authenticatorClassname($container);
                        }

                        $middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
                            'authenticator' => $apiKeyAuthenticator,
                            'regex' => '/^(.*)$/i',
                            'header' => $authMethod['isKeyInHeader'] ? $authMethod['keyParamName'] : null,
                            'parameter' => $authMethod['isKeyInQuery'] ? $authMethod['keyParamName'] : null,
                            'cookie' => $authMethod['isKeyInCookie'] ? $authMethod['keyParamName'] : null,
                            'argument' => null,
                        ]));
                        break;
                    case 'oauth2':
                        $authenticatorClassname = "\\{$authPackage}\\OAuthAuthenticator";
                        if (class_exists($authenticatorClassname)) {
                            $oAuthAuthenticator = new $authenticatorClassname($container, $authMethod['scopes']);
                        }

                        $middlewares[] = new TokenAuthentication($this->getTokenAuthenticationOptions([
                            'authenticator' => $oAuthAuthenticator,
                            'regex' => '/Bearer\s+(.*)$/i',
                            'header' => 'Authorization',
                            'parameter' => null,
                            'cookie' => null,
                            'argument' => null,
                        ]));
                        break;
                    default:
                        throw new Exception('Unknown authorization schema type');
                }
            }

            $this->addRoute(
                [$operation['httpMethod']],
                "{$operation['basePathWithoutHost']}{$operation['path']}",
                $callback,
                $middlewares
            )->setName($operation['operationId']);
        }
    }

    /**
     * Merges user defined options with dynamic params
     *
     * @param array $options Params which need to merge into user options
     *
     * @return array Merged array
     */
    private function getTokenAuthenticationOptions(array $options)
    {
        if (is_array($this->slimApp->getContainer()['tokenAuthenticationOptions']) === false) {
            return $options;
        }

        return array_merge($this->slimApp->getContainer()['tokenAuthenticationOptions'], $options);
    }

    /**
     * Add route with multiple methods
     *
     * @param string[]        $methods     Numeric array of HTTP method names
     * @param string          $pattern     The route URI pattern
     * @param callable|string $callable    The route callback routine
     * @param array|null      $middlewares List of middlewares
     *
     * @return RouteInterface
     *
     * @throws InvalidArgumentException If the route pattern isn't a string
     */
    public function addRoute(array $methods, string $pattern, $callable, $middlewares = [])
    {
        $route = $this->slimApp->map($methods, $pattern, $callable);
        foreach ($middlewares as $middleware) {
            $route->add($middleware);
        }
        return $route;
    }

    /**
     * Returns Slim Framework instance
     *
     * @return App
     */
    public function getSlimApp()
    {
        return $this->slimApp;
    }
}
