<?php
use Luticate\Utils\Business\LuBusinessException;
use Luticate\Utils\Controller\LuRoute;
use Luticate\Utils\Dbo\LuDboConstraintException;
use Luticate\Utils\Dbo\LuRouteDbo;
use Luticate\Utils\Dbo\LuStringDbo;
use Luticate\Utils\Middleware\LuAbstractMiddleware;

/**
 * Created by PhpStorm.
 * User: robin
 * Date: 6/7/16
 * Time: 2:33 AM
 */

class BasicTestController
{
    public function testBasic()
    {
        return "Test. succeeded";
    }
    
    public function testParameter($test)
    {
        return $test;
    }

    public function testTypedParameter(TestDbo $test)
    {
        return $test->getTest();
    }

    /**
     * @param TestDbo $test
     * @ensure42
     * @return string
     */
    public function testTypedParameterDoc(TestDbo $test)
    {
        return $test->getTest();
    }

    /**
     * @param TestDbo $test
     * @nullable
     * @return string
     */
    public function testNullable(TestDbo $test = null)
    {
        return is_null($test);
    }

    public function testBeforeMiddleware(LuStringDbo $_user)
    {
        return $_user->getString();
    }

    public function testAfterMiddleware()
    {
        return "A result";
    }
}

class BeforeMiddleware extends LuAbstractMiddleware {

    public function onBefore(LuRouteDbo $route, $parameters)
    {
        $parameters["_user"] = "\"my user\"";
        return $parameters;
    }

    public function onAfter(LuRouteDbo $route, $parameters, $result)
    {
        return $result;
    }
}

class AfterMiddleware extends LuAbstractMiddleware {

    public function onBefore(LuRouteDbo $route, $parameters)
    {
        return $parameters;
    }

    public function onAfter(LuRouteDbo $route, $parameters, $result)
    {
        return "AfterMiddleware";
    }
}

class AuthenticationMiddleware extends LuAbstractMiddleware {

    public function onBefore(LuRouteDbo $route, $parameters)
    {
        $permissions = $parameters['_middleware']['permissions'] ?? [];
        if ($permissions[0] != "ALLOWED") {
            throw new LuBusinessException("Unauthorized", 401);
        }
        return $parameters;
    }

    public function onAfter(LuRouteDbo $route, $parameters, $result)
    {
        return $result;
    }
}

class LuRouteTest extends \PHPUnit_Framework_TestCase
{
    public function testBasic()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testBasic");
        $router->setup();
        $res = $router->dispatch("GET", "/test", []);
        $this->assertSame("Test. succeeded", $res);
    }
    
    public function testParameter()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testParameter");
        $router->setup();
        $res = $router->dispatch("GET", "/test", ["test" => "A value"]);
        $this->assertSame("A value", $res);
    }

    public function testTypedParameter()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testTypedParameter");
        $router->setup();
        $res = $router->dispatch("GET", "/test", ["test" => '{"Test": "Some value"}']);
        $this->assertSame("Some value", $res);
    }

    public function testTypedParameterDoc()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testTypedParameterDoc");
        $router->setup();
        $res = $router->dispatch("GET", "/test", ["test" => '{"Test": "42"}']);
        $this->assertSame("42", $res);
    }

    public function testNullable()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testNullable");
        $router->setup();
        $res = $router->dispatch("GET", "/test", ["test" => 'null']);
        $this->assertSame(true, $res);
    }

    public function testBeforeMiddleware()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testBeforeMiddleware", [], ['BeforeMiddleware']);
        $router->setup();
        $res = $router->dispatch("GET", "/test", []);
        $this->assertSame("my user", $res);
    }

    public function testAfterMiddleware()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testAfterMiddleware", [], ['AfterMiddleware']);
        $router->setup();
        $res = $router->dispatch("GET", "/test", []);
        $this->assertSame("AfterMiddleware", $res);
    }

    public function testAuthenticationMiddlewareAllowed()
    {
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testBasic",
            ["permissions" => ["ALLOWED"]], ['AuthenticationMiddleware']);
        $router->setup();
        $res = $router->dispatch("GET", "/test", []);
        $this->assertSame("Test. succeeded", $res);
    }

    public function testAuthenticationMiddlewareNotAllowed()
    {
        $this->expectException(LuBusinessException::class);
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testBasic",
            ["permissions" => ["NOT_ALLOWED"]], ['AuthenticationMiddleware']);
        $router->setup();
        $router->dispatch("GET", "/test", []);
    }

    public function testTypedParameterDocConstraint()
    {
        $this->expectException(LuBusinessException::class);
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testTypedParameterDoc");
        $router->setup();
        $router->dispatch("GET", "/test", ["test" => '{"Test": "Test."}']);
    }

    public function testMethodNotAllowed()
    {
        $this->expectException(LuBusinessException::class);
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testTypedParameterDoc");
        $router->setup();
        $router->dispatch("POST", "/test", ["test" => '{"Test": "Test."}']);
    }

    public function testRouteNotFound()
    {
        $this->expectException(LuBusinessException::class);
        $router = new LuRoute();
        $router->addRoute("GET", "/test", '\BasicTestController', "testTypedParameterDoc");
        $router->setup();
        $router->dispatch("GET", "/notfound", ["test" => '{"Test": "Test."}']);
    }
}