1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 
<?php

declare(strict_types=1);

namespace Wtf;

use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;

/**
 * Wtf Service Provider.
 */
class Provider implements ServiceProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public function register(Container $container): void
    {
        $container['suit_config'] = function ($c) {
            return new Config($c);
        };
        $container['config'] = $container->protect(function (string $string, $default = null) use ($container) {
            return $container['suit_config']->__invoke($string, $default);
        });
        $container['app_router'] = function ($c) {
            return new Router($c);
        };
        $container['globalrequest_middleware'] = $container->protect(function ($request, $response, $next) use ($container) {
            if ($container->has('request')) {
                unset($container['request']);
                $container['request'] = $request;
            }

            return $next($request, $response);
        });
        $container['sentry'] = $this->getSentry($container);
        $container['controller'] = $this->setControllerLoader($container);
        $container['errorHandler'] = $this->setErrorHandler($container);
        $container['phpErrorHandler'] = $this->setErrorHandler($container);
    }

    /**
     * Add Sentry integration.
     */
    protected function getSentry(Container $container): callable
    {
        return function ($c) {
            $config = $c['config']('suit.sentry');

            $client = new \Raven_Client($config['dsn'], $config['options'] ?? []);
            $client->install();
            if ($c->has('user')) {
                $client->user_context((array) $c->get('user'));
            }

            return $client;
        };
    }

    /**
     * Set controller() function into container.
     *
     * @param Container $container
     *
     * @return callable
     */
    protected function setControllerLoader(Container $container): callable
    {
        return $container->protect(function (string $name) use ($container) {
            $parts = \explode('_', $name);
            $class = $container['config']('suit.namespaces.controller', '\\App\\Controller\\');
            foreach ($parts as $part) {
                $class .= \ucfirst($part);
            }
            if (!$container->has('controller_'.$class)) {
                $container['controller_'.$class] = function ($container) use ($class) {
                    return new $class($container);
                };
            }

            return $container['controller_'.$class];
        });
    }

    /**
     * Set error handler with sentry.
     *
     * @param Container $container
     *
     * @return callable
     */
    protected function setErrorHandler(Container $container): callable
    {
        return function (Container $container) {
            return function (ServerRequestInterface $request, ResponseInterface $response, Throwable $e) use ($container) {
                $container->sentry->captureException($e);
                if ($container->has('appErrorHandler')) {
                    return $container['appErrorHandler']->error500($request, $response, $e);
                }

                return $response->withStatus(500);
            };
        };
    }
}