<?php

namespace Authstack\OpenIDConnect\Tests;

use Illuminate\Http\Response;
use PHPUnit\Framework\TestCase;
use Authstack\OpenIDConnect\Client;
use Authstack\OpenIDConnect\Provider;
use Illuminate\Http\RedirectResponse;
use Authstack\OpenIDConnect\Model\Protocol\Flow\AuthorizationCode;

class ClientTest extends TestCase
{
    public function testCreateClient()
    {
        $client = Client::create([
            'clientID' => '1234567890100000011111111111',
            'secret'=> sha1('secret'),
            'redirectURI' => 'https://redirect-to.relying-party.tld'
        ]);

        $this->assertInstanceOf(Client::class, $client);
        $this->assertTrue(16 === strlen($client->getNonce()));
        $this->assertEquals(sha1('secret'), $client->getClientSecret());
    }

    public function testAuthenticationRequest()
    {
        $client = $this->getClient();
        $provider = $this->getProvider();
        $flow = new AuthorizationCode();

        $flow = $client
            ->setProvider($provider)
            ->setFlow($flow)
            ->buildAuthenticationRequest();

        $this->assertTrue(is_array($flow->getQuery()));
        $this->assertStringStartsWith('https://authstack.dev/oauth2/authorize', $flow->getRedirectURI());
        $this->assertInstanceOf(RedirectResponse::class, $client->authenticate());
    }

    public function testAuthenticationWithPKCE()
    {
        $client = $this->getClient();
        $provider = $this->getProvider();
        $flow = new AuthorizationCode();

        // Turn PKCE on
        $flow->setPKCE(true);

        // Black magic
        /** @var  $flow AuthorizationCode */
        $flow = $client
                    ->setProvider($provider)
                    ->setFlow($flow)
                    ->buildAuthenticationRequest();

        // Get the code_verifier (secret) and save it to permanent storage. You need this when exchanging code for id_token
        $code_verifier = $flow->getCodeVerifier();

        $this->assertTrue($flow->usesPKCE());
        $this->assertTrue(is_string($code_verifier));
        $this->assertArrayHasKey('code_challenge', $flow->getQuery());
        $this->assertInstanceOf(RedirectResponse::class, $flow->getResponse());
    }

    public function testAuthenticationWithAdditionalParams()
    {
        $client = $this->getClient();
        $provider = $this->getProvider();
        $flow = new AuthorizationCode();

        $client->setQueryParam('server_id', str_random(16));
        $client->setQueryParam('state', str_random(16));

        $flow = $client
            ->setProvider($provider)
            ->setFlow($flow)
            ->buildAuthenticationRequest();

        $query = $flow->getQuery();

        $this->assertArrayHasKey('server_id', $query);
        $this->assertArrayHasKey('state', $query);
    }

    /**
     * @return Client
     */
    protected function getClient(): Client
    {
        return Client::create([
            'clientID' => '12345',
            'secret' => sha1('secret'),
            'redirectURI' => 'https://rp.tld/oidc/callback'
        ]);
    }

    /**
     * @return Provider
     */
    protected function getProvider(): Provider
    {
        return Provider::create([
            'iss' => 'https://authstack.dev',
            'authorizationEndpoint' => 'https://authstack.dev/oauth2/authorize',
            'tokenEndpoint' => 'https://authstack.dev/api/v1/oauth2/token'
        ]);
    }
}