<?php

namespace Tests\unit\workflow\engine\src\ProcessMaker\Services\OAuth2;

use \OAuth2\Response as OAuth2Response;
use ProcessMaker\Model\LicenseManager;
use ProcessMaker\Model\OauthClients;
use ProcessMaker\Model\User;
use ProcessMaker\Services\OAuth2\Server;
use Tests\MockPhpStream;
use Tests\TestCase;

class ServerTest extends TestCase
{
    private $server;

    /**
     * Setup method.
     */
    public function setUp(): void
    {
        parent::setUp();
        $this->server = $_SERVER;
        $_SERVER['CONTENT_TYPE'] = 'application/json';
        $_SERVER['REQUEST_METHOD'] = 'POST';
        $this->createTestLicense();
    }

    /**
     * Teardown method.
     */
    public function tearDown(): void
    {
        parent::tearDown();
        $_SERVER = $this->server;
    }

    /**
     * Test the "postToken()" method with valid credentials.
     * @test
     * @covers \ProcessMaker\Services\OAuth2\Server::postToken()
     */
    public function it_should_test_post_token_with_valid_credentials()
    {
        $user = User::where('USR_ID', '=', 1)->get()->first();
        $oauthClients = OauthClients::factory()->create([
            "USR_UID" => $user->USR_UID
        ]);

        $data = '{
            "grant_type":"password",
            "scope":"*",
            "client_id":"' . $oauthClients->CLIENT_ID . '",
            "client_secret":"' . $oauthClients->CLIENT_SECRET . '",
            "username":"' . $user->USR_USERNAME . '",
            "password":"admin"
        }';
        stream_wrapper_unregister("php");
        stream_wrapper_register("php", "Tests\MockPhpStream");
        file_put_contents('php://input', $data);

        list($host, $port) = strpos(DB_HOST, ':') !== false ? explode(':', DB_HOST) : [DB_HOST, ''];
        $port = empty($port) ? '' : ";port=$port";

        Server::setDatabaseSource(DB_USER, DB_PASS, DB_ADAPTER . ":host=$host;dbname=" . DB_NAME . $port);

        $server = new Server();
        $server->postToken();
        stream_wrapper_restore("php");
        $response = ob_get_contents();
        ob_clean();
        $response = json_decode($response, true);
        $this->assertNotEmpty($response);
    }

    /**
     * Test the method "postToken()" with the return parameter of the object "OAuth2\Response".
     * @test
     * @covers \ProcessMaker\Services\OAuth2\Server::postToken()
     */
    public function it_should_test_post_token_with_return_handle_token()
    {
        $user = User::where('USR_ID', '=', 1)->get()->first();
        $oauthClients = OauthClients::factory()->create([
            "USR_UID" => $user->USR_UID
        ]);

        $data = '{
            "grant_type":"password",
            "scope":"*",
            "client_id":"' . $oauthClients->CLIENT_ID . '",
            "client_secret":"' . $oauthClients->CLIENT_SECRET . '",
            "username":"' . $user->USR_USERNAME . '",
            "password":"admin"
        }';
        stream_wrapper_unregister("php");
        stream_wrapper_register("php", "Tests\MockPhpStream");
        file_put_contents('php://input', $data);

        list($host, $port) = strpos(DB_HOST, ':') !== false ? explode(':', DB_HOST) : [DB_HOST, ''];
        $port = empty($port) ? '' : ";port=$port";

        Server::setDatabaseSource(DB_USER, DB_PASS, DB_ADAPTER . ":host=$host;dbname=" . DB_NAME . $port);

        $server = new Server();
        $result = $server->postToken(null, true);
        stream_wrapper_restore("php");

        $this->assertTrue($result instanceof OAuth2Response);
    }

    /**
     * Test the method "postToken()" with empty clientId.
     * @test
     * @covers \ProcessMaker\Services\OAuth2\Server::postToken()
     */
    public function it_should_test_post_token_with_empty_client_id()
    {
        $user = User::where('USR_ID', '=', 1)->get()->first();
        $oauthClients = OauthClients::factory()->create([
            "USR_UID" => $user->USR_UID
        ]);

        $data = '{
            "grant_type":"password",
            "scope":"*",
            "client_id":"",
            "client_secret":"",
            "username":"' . $user->USR_USERNAME . '",
            "password":"admin"
        }';
        stream_wrapper_unregister("php");
        stream_wrapper_register("php", "Tests\MockPhpStream");
        file_put_contents('php://input', $data);

        list($host, $port) = strpos(DB_HOST, ':') !== false ? explode(':', DB_HOST) : [DB_HOST, ''];
        $port = empty($port) ? '' : ";port=$port";

        Server::setDatabaseSource(DB_USER, DB_PASS, DB_ADAPTER . ":host=$host;dbname=" . DB_NAME . $port);

        $server = new Server();
        $server->postToken();
        stream_wrapper_restore("php");
        $response = ob_get_contents();
        ob_clean();
        $response = json_decode($response, true);
        $this->assertArrayHasKey("error", $response);
        $this->assertArrayHasKey("error_description", $response);
    }

    /**
     * Test the method "postToken()" for the x-pm-local-client.
     * @test
     * @covers \ProcessMaker\Services\OAuth2\Server::postToken()
     */
    public function it_should_test_post_token_for_pm_client_id()
    {
        $user = User::where('USR_ID', '=', 1)->get()->first();
        $oauthClients = OauthClients::where('CLIENT_ID', '=', 'x-pm-local-client')->get()->first();

        $data = '{
            "grant_type":"password",
            "scope":"*",
            "client_id":"' . $oauthClients->CLIENT_ID . '",
            "client_secret":"' . $oauthClients->CLIENT_SECRET . '",
            "username":"' . $user->USR_USERNAME . '",
            "password":"admin"
        }';
        stream_wrapper_unregister("php");
        stream_wrapper_register("php", "Tests\MockPhpStream");
        file_put_contents('php://input', $data);

        list($host, $port) = strpos(DB_HOST, ':') !== false ? explode(':', DB_HOST) : [DB_HOST, ''];
        $port = empty($port) ? '' : ";port=$port";

        Server::setDatabaseSource(DB_USER, DB_PASS, DB_ADAPTER . ":host=$host;dbname=" . DB_NAME . $port);
        Server::setPmClientId('x-pm-local-client');
        $server = new Server();
        $server->postToken();
        stream_wrapper_restore("php");
        $response = ob_get_contents();
        ob_clean();
        $response = json_decode($response, true);
        $this->assertNotEmpty($response);
    }

    /**
     * Create test license
     */
    private function createTestLicense()
    {
        if (!is_dir(PATH_DB)) {
            mkdir(PATH_DB);
        }
        $pathWorkspace = PATH_DB . config('system.workspace') . PATH_SEP;
        if (!is_dir($pathWorkspace)) {
            mkdir($pathWorkspace);
        }
        $info = $this->getLicenseInfo();
        $filename = $pathWorkspace . $info['name'];
        $data = $info['content'];
        file_put_contents($filename, $data);

        LicenseManager::truncate();
        LicenseManager::factory()->create([
            "LICENSE_DATA" => $data,
            "LICENSE_PATH" => $filename,
            "LICENSE_WORKSPACE" => env('MAIN_SYS_SYS')
        ]);
    }

    /**
     * Get test license information.
     * @return array
     */
    private function getLicenseInfo()
    {
        $content = "-------------------------------BEGIN LICENSE KEY--------------------------------
[encoded data, 23323 characters]
--------------------------------END LICENSE KEY---------------------------------";
        return [
            "name" => "license_7ubh3dHS2+bWqaOlnZ0.dat",
            "content" => $content
        ];
    }
}
