<?php

namespace Tests\unit\workflow\engine\classes;

use Cases;
use Derivation;
use G;
use Illuminate\Support\Facades\DB;
use ProcessMaker\Model\Application;
use ProcessMaker\Model\Delegation;
use ProcessMaker\Model\Process;
use ProcessMaker\Model\Route;
use ProcessMaker\Model\SubApplication;
use ProcessMaker\Model\Task;
use ProcessMaker\Model\TaskUser;
use ProcessMaker\Model\User;
use Tests\CreateTestSite;
use Tests\TestCase;

class DerivationTest extends TestCase
{
    use CreateTestSite;
    
    /**
     * Call the setUp parent method
     */
    public function setUp(): void
    {
        parent::setUp(); // TODO: Change the autogenerated stub

        // Truncate the APP_SEQUENCE table
        DB::table('APP_SEQUENCE')->truncate();

        config(["system.workspace" => "new_site"]);
        $workspace = config("system.workspace");
        $this->createDBFile($workspace);
    }

    /**
     * Call the tearDown method
     */
    public function tearDown(): void
    {
        parent::tearDown(); // TODO: Change the autogenerated stub
    }

    /**
     * It tests the getSubProcessVariables method with object variables
     *
     * @covers Derivation::getSubProcessVariables()
     * @test
     */
    public function it_should_test_the_get_sub_process_variables_method_with_object_variables()
    {
        $fields = ['@&var2' => '@&var3'];
        $childCaseData = ['var2' => (object)['Street' => 'test', 'name' => 'Something']];
        $parentCaseData = ['var1' => (object)['Street' => 'test']];

        $der = new Derivation();
        $res = $der->getSubProcessVariables($fields, $childCaseData, $parentCaseData);

        $this->assertArrayHasKey('var3', $res);
        $this->assertObjectHasAttribute('Street', $res['var3']);
        $this->assertObjectHasAttribute('name', $res['var3']);
        $this->assertEquals($res['var3'], (object)['Street' => 'test', 'name' => 'Something']);
    }

    /**
     * It tests the getSubProcessVariables method with origin labels
     *
     * @covers Derivation::getSubProcessVariables()
     * @test
     */
    public function it_should_test_the_get_sub_process_variables_method_with_origin_labels()
    {
        $fields = ['@&var2' => '@&var3', '@&var2_label' => '@&var3'];
        $childCaseData = [
            'var2' => (object)['Street' => 'test', 'name' => 'Something'],
            'var2_label' => ['Street' => 'test', 'name' => 'Something']
        ];
        $parentCaseData = ['var1' => (object)['Street' => 'test']];

        $der = new Derivation();
        $res = $der->getSubProcessVariables($fields, $childCaseData, $parentCaseData);

        $this->assertArrayHasKey('var3_label', $res);
        $this->assertEquals($res['var3_label'], ['Street' => 'test', 'name' => 'Something']);
    }

    /**
     * It tests the getSubProcessVariables method with target labels
     *
     * @covers Derivation::getSubProcessVariables()
     * @test
     */
    public function it_should_test_the_get_sub_process_variables_method_with_target_labels()
    {
        $fields = ['@&var2' => '@&var3', '@&var2' => '@&var3_label'];
        $childCaseData = ['var2' => (object)['Street' => 'test', 'name' => 'Something']];
        $parentCaseData = ['var1' => (object)['Street' => 'test']];

        $der = new Derivation();
        $res = $der->getSubProcessVariables($fields, $childCaseData, $parentCaseData);

        $this->assertArrayHasKey('var3_label', $res);
        $this->assertObjectHasAttribute('Street', $res['var3_label']);
        $this->assertObjectHasAttribute('name', $res['var3_label']);
        $this->assertEquals($res['var3_label'], (object)['Street' => 'test', 'name' => 'Something']);
    }

    /**
     * It tests the doDerivation method sending variables synchronously
     *
     * @covers Derivation::doDerivation()
     * @covers Derivation::<protected>
     * 
     * @test
     */
    public function it_should_test_the_do_derivation_method_sending_variables_synchronously()
    {
        // Create the models
        $user = User::factory()->create();
        $process = Process::factory()->create([
            'PRO_CREATE_USER' => $user->USR_UID
        ]);
        $task = Task::factory()->create([
            'PRO_UID' => $process->PRO_UID,
        ]);
        $application = Application::factory()->create([
            'PRO_UID' => $process->PRO_UID,
            'APP_INIT_USER' => $user->USR_UID,
            'APP_CUR_USER' => $user->USR_UID
        ]);
        $appDelegation = Delegation::factory()->create([
            'APP_UID' => $application->APP_UID,
            'APP_NUMBER' => $application->APP_NUMBER
        ]);
        SubApplication::factory()->create([
            'APP_UID' => $application->APP_UID,
            'APP_PARENT' => $application->APP_UID,
            'DEL_INDEX_PARENT' => $appDelegation->DEL_INDEX
        ]);

        // Create the parameters
        $currentDelegation = [
            'APP_UID' => $application->APP_UID,
            'DEL_INDEX' => $appDelegation->DEL_INDEX,
            'ROU_TYPE' => ''
        ];
        $nextDel = [
            'TAS_UID' => $task->TAS_UID,
            'USR_UID' => $user->USR_UID,
            'DEL_PRIORITY' => $appDelegation->DEL_PRIORITY,
            'TAS_ASSIGN_TYPE' => $task->TAS_ASSIGN_TYPE,
            'TAS_ID' => $task->TAS_ID
        ];
        $appFields = [
            'APP_NUMBER' => $application->APP_NUMBER,
            'DEL_INDEX' => $appDelegation->DEL_INDEX,
            'DEL_THREAD' => $appDelegation->DEL_THREAD,
            'PRO_UID' => $process->PRO_UID,
            'PRO_ID' => $process->PRO_ID,
            'APP_DATA' => [],
            'APP_TITLE' => $application->APP_TITLE,
            'APP_UID' => $application->APP_UID
        ];
        $sp = [
            'SP_VARIABLES_OUT' => 'a:1:{s:6:"@&var1";s:6:"@&var2";}',
            'SP_VARIABLES_IN' => 'a:1:{s:6:"@&var2";s:6:"@&var3";}',
            'SP_SYNCHRONOUS' => 1,
            'SP_TYPE' => '',
            'TAS_UID' => $task->TAS_UID,
            'USR_UID' => $user->USR_UID,
        ];

        // Create the Derivation object
        $der = new Derivation();
        $der->case = new Cases;

        // Call the doDerivation method
        $res = $der->doDerivation($currentDelegation, $nextDel, $appFields, $sp);

        // Assert the new delegation index is 1
        $this->assertTrue($res >= 1);

        // Review the subprocess synchronously
        $query = SubApplication::query()->select();
        $query->where('APP_PARENT', $application->APP_UID);
        $query->where('DEL_INDEX_PARENT', $appDelegation->DEL_INDEX);
        $results = $query->get()->toArray();
        $this->assertNotEmpty($results);
        $this->assertEquals($results[0]['SA_STATUS'], 'ACTIVE');
    }

    /**
     * It tests the doDerivation method sending variables asynchronously
     *
     * @covers Derivation::doDerivation()
     * @covers Derivation::<protected>
     * 
     * @test
     */
    public function it_should_test_the_do_derivation_method_sending_variables_asynchronously()
    {
        // Create the models
        $user = User::factory()->create();
        $process = Process::factory()->create([
            'PRO_CREATE_USER' => $user->USR_UID
        ]);
        $task = Task::factory()->create([
            'PRO_UID' => $process->PRO_UID,
            'TAS_USER' => $user->USR_UID
        ]);
        TaskUser::factory()->create([
            'TAS_UID' => $task->TAS_UID,
            'USR_UID' => $user->USR_UID
        ]);
        $application = Application::factory()->create([
            'PRO_UID' => $process->PRO_UID,
            'APP_INIT_USER' => $user->USR_UID,
            'APP_CUR_USER' => $user->USR_UID
        ]);
        $appDelegation = Delegation::factory()->create([
            'TAS_UID' => $task->TAS_UID,
            'APP_UID' => $application->APP_UID,
            'APP_NUMBER' => $application->APP_NUMBER
        ]);
        SubApplication::factory()->create([
            'APP_UID' => $application->APP_UID,
            'APP_PARENT' => $application->APP_UID,
            'DEL_INDEX_PARENT' => $appDelegation->DEL_INDEX,
            'SA_STATUS' => 'FINISHED'
        ]);
        Route::factory()->create([
            'TAS_UID' => $task->TAS_UID,
            'ROU_NEXT_TASK' => $task->TAS_UID,
            'PRO_UID' => $process->PRO_UID
        ]);

        // Create the parameters
        $currentDelegation = [
            'APP_UID' => $application->APP_UID,
            'DEL_INDEX' => $appDelegation->DEL_INDEX,
            'ROU_TYPE' => '',
            'TAS_UID' => $task->TAS_UID
        ];
        $nextDel = [
            'TAS_UID' => $task->TAS_UID,
            'USR_UID' => $user->USR_UID,
            'DEL_PRIORITY' => $appDelegation->DEL_PRIORITY,
            'TAS_ASSIGN_TYPE' => $task->TAS_ASSIGN_TYPE,
            'TAS_ID' => $task->TAS_ID
        ];
        $appFields = [
            'APP_NUMBER' => $application->APP_NUMBER,
            'DEL_THREAD' => $appDelegation->DEL_THREAD,
            'PRO_UID' => $process->PRO_UID,
            'PRO_ID' => $process->PRO_ID,
            'APP_DATA' => [],
            'APP_TITLE' => $application->APP_TITLE,
            'APP_UID' => $application->APP_UID
        ];
        $sp = [
            'SP_VARIABLES_OUT' => 'a:1:{s:6:"@&var1";s:6:"@&var2";}',
            'SP_VARIABLES_IN' => 'a:1:{s:6:"@&var2";s:6:"@&var3";}',
            'SP_SYNCHRONOUS' => 0,
            'SP_TYPE' => '',
            'TAS_UID' => $task->TAS_UID,
            'USR_UID' => $user->USR_UID,
        ];

        // Create the Derivation object
        $der = new Derivation();
        $der->case = new Cases;

        // Call the doDerivation method
        $res = $der->doDerivation($currentDelegation, $nextDel, $appFields, $sp);

        // Assert the new delegation index is 1
        $this->assertTrue($res >= 1);

        // Review the subprocess asynchronously
        $query = SubApplication::query()->select();
        $query->where('APP_PARENT', $application->APP_UID);
        $query->where('DEL_INDEX_PARENT', $appDelegation->DEL_INDEX);
        $results = $query->get()->toArray();
        $this->assertNotEmpty($results);
        $this->assertEquals($results[0]['SA_STATUS'], 'FINISHED');
    }
}