<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Tests\Security\PasswordRecover;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\PasswordRecover\TokenManager;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * @internal
 *
 * @coversNothing
 */
final class TokenManagerTest extends KernelTestCase
{
    protected $tokenManager;

    protected function setUp(): void
    {
        self::bootKernel();

        $logger = self::getContainer()
            ->get('logger');

        $this->tokenManager = new TokenManager('secret', $logger);
    }

    public static function setUpBefore() {}

    public function testGenerate()
    {
        $tokenManager = $this->tokenManager;
        $user = (new User())->setUsernameCanonical('test');
        $expiration = new \DateTimeImmutable('tomorrow');

        $tokens = $tokenManager->generate($user, $expiration);

        $this->assertIsArray($tokens);
        $this->assertArrayHasKey('h', $tokens);
        $this->assertArrayHasKey('t', $tokens);
        $this->assertNotEmpty($tokens['h']);
        $this->assertNotEmpty($tokens['t']);
        $this->assertEquals($user->getUsernameCanonical(), $tokens['u']);
    }

    public function testGenerateEmptyUsernameCanonical()
    {
        $this->expectException(\UnexpectedValueException::class);

        $tokenManager = $this->tokenManager;
        // set a username, but not a username canonical
        $user = (new User())->setUsername('test');
        $expiration = new \DateTimeImmutable('tomorrow');

        $tokenManager->generate($user, $expiration);
    }

    public function testVerify()
    {
        $tokenManager = $this->tokenManager;
        $user = (new User())->setUsernameCanonical('test');
        $expiration = new \DateTimeImmutable('tomorrow');

        $tokens = $tokenManager->generate($user, $expiration);

        $hash = $tokens[TokenManager::HASH];
        $token = $tokens[TokenManager::TOKEN];
        $timestamp = $tokens[TokenManager::TIMESTAMP];

        $verification = $tokenManager->verify($hash, $token, $user, $timestamp);

        $this->assertTrue($verification);

        // test with altering token
        $this->assertFalse($tokenManager->verify($hash.'5', $token, $user, $timestamp));
        $this->assertFalse($tokenManager->verify($hash, $token.'25', $user, $timestamp));
        $this->assertFalse($tokenManager->verify($hash, $token, $user->setUsernameCanonical('test2'), $timestamp));
        $this->assertFalse($tokenManager->verify($hash, $token, $user, (string) ($timestamp + 1)));
    }

    public function testVerifyExpiredFails()
    {
        $tokenManager = $this->tokenManager;
        $user = (new User())->setUsernameCanonical('test');
        $expiration = (new \DateTimeImmutable('now'))->sub(new \DateInterval('PT1S'));

        $tokens = $tokenManager->generate($user, $expiration);

        $hash = $tokens[TokenManager::HASH];
        $token = $tokens[TokenManager::TOKEN];
        $timestamp = $tokens[TokenManager::TIMESTAMP];

        $verification = $tokenManager->verify($hash, $token, $user, $timestamp);

        $this->assertFalse($verification);
    }
}
