<?php

namespace AuthStack\OAuth2\Model\Protocol\Grant\tests\units;

use mageekguy\atoum;
use Illuminate\Http\Request;
use AuthStack\OAuth2\Exception\OAuth2Exception;
use AuthStack\OAuth2\Model\Protocol\Grant\GrantFactory;
use AuthStack\OAuth2\Model\Protocol\Request\Implicit\GrantRequest;
use AuthStack\OAuth2\Model\Protocol\Response\Implicit\GrantResponse;
use AuthStack\OAuth2\Model\Protocol\Grant\Implicit as ImplicitGrant;

class Implicit extends atoum\test
{
    public function testCreateImplicitGrantRequest()
    {
        $this
            ->given($implicit = (new GrantFactory())->implicit())
            ->then
                ->object($implicit)->isInstanceOf(ImplicitGrant::class)
                ->object($request = $implicit->grantRequest())->isInstanceOf(GrantRequest::class)
                ->object($request
                    ->setClientID('client ID')
                    ->setRedirectUri('https://my.relying.party/callback')
                    ->setScope('read write')
                    ->setState('my state')
                )->isInstanceOf(GrantRequest::class)
                ->array($request->toArray())
                    ->hasKeys(['response_type', 'client_id', 'redirect_uri', 'scope', 'state'])
                    ->strictlyContainsValues([
                        'response_type' => 'token',
                        'client_id' => 'client ID',
                        'redirect_uri' => 'https://my.relying.party/callback',
                        'scope' => 'read write',
                        'state' => 'my state'
                    ])
            ;
    }

    public function testReceiveImplicitGrantRequest()
    {
        $this->given($model = [
            'response_type' => 'token',
            'client_id' => 'client ID',
            'redirect_uri' => 'https://my.relying.party/callback',
            'scope' => 'read write',
            'state' => 'my state'
            ])
            ->and($request = Request::create('https://your.authorization.server/authorize', 'GET', $model))
            ->then
                ->object($implicit = (new GrantFactory())->implicit())->isInstanceOf(ImplicitGrant::class)
                ->object($req = $implicit->grantRequest()->receive($request))->isInstanceOf(GrantRequest::class)
                ->string($req->getClientID())->isEqualTo('client ID')
                ->string($req->getRedirectURI())->isEqualTo('https://my.relying.party/callback')
                ->string($req->getScope())->isEqualTo('read write')
                ->string($req->getState())->isEqualTo('my state')
            ;
    }

    public function testReceiveImplicitGrantRequestFails()
    {
        $this->given($model = [
            'response_type' => 'code', // <- this is wrong, it should be 'token' for the implicit grant to validate
            'client_id' => 'client ID',
            'redirect_uri' => 'https://my.relying.party/callback',
            'scope' => 'read write',
            'state' => 'my state'
        ])
            ->and($request = Request::create('https://your.authorization.server/authorize', 'GET', $model))
            ->then
                ->exception(function() use ($request)
                {
                    $received = (new GrantFactory())->implicit()->grantRequest()->receive($request);
                })
                ->isInstanceOf(OAuth2Exception::class)
        ;
    }

    public function testCreateImplicitGrantResponse()
    {
        $this->given($response = (new GrantFactory())->implicit()->grantResponse())
            ->then
                ->object($response)->isInstanceOf(GrantResponse::class)
                ->object(
                    $response->setAccessToken('hello_world')
                        ->setTokenType('bearer')
                        ->setExpiresIn(300)
                        ->setScope('read write')
                        ->setState('my state')
                )->isInstanceOf(GrantResponse::class)
                ->array($response->toArray())->hasKeys(['access_token', 'token_type', 'expires_in', 'scope', 'state'])
                ->strictlyContainsValues([
                    'access_token' => 'hello_world',
                    'token_type' => 'bearer',
                    'expires_in' => 300,
                    'scope' => 'read write',
                    'state' => 'my state'
                ])
            ;
    }
}
