<?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\Entity\Workflow;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\PersonBundle\Entity\Person;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 *
 * @coversNothing
 */
final class EntityWorkflowTest extends TestCase
{
    public function testIsFinalizeWith1Steps()
    {
        $entityWorkflow = new EntityWorkflow();

        $entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow), 'finalize', new \DateTimeImmutable());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        $this->assertTrue($entityWorkflow->isFinal());
    }

    public function testIsFinalizeWith4Steps()
    {
        $entityWorkflow = new EntityWorkflow();

        $this->assertFalse($entityWorkflow->isFinal());

        $entityWorkflow->setStep('two', new WorkflowTransitionContextDTO($entityWorkflow), 'two', new \DateTimeImmutable());

        $this->assertFalse($entityWorkflow->isFinal());

        $entityWorkflow->setStep('previous_final', new WorkflowTransitionContextDTO($entityWorkflow), 'three', new \DateTimeImmutable());

        $this->assertFalse($entityWorkflow->isFinal());

        $entityWorkflow->getCurrentStep()->setIsFinal(true);
        $entityWorkflow->setStep('final', new WorkflowTransitionContextDTO($entityWorkflow), 'four', new \DateTimeImmutable());

        $this->assertTrue($entityWorkflow->isFinal());
    }

    public function testIsFreeze()
    {
        $entityWorkflow = new EntityWorkflow();

        $this->assertFalse($entityWorkflow->isFreeze());

        $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_one', new \DateTimeImmutable());

        $this->assertFalse($entityWorkflow->isFreeze());

        $entityWorkflow->setStep('step_three', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_three', new \DateTimeImmutable());

        $this->assertFalse($entityWorkflow->isFreeze());

        $entityWorkflow->setStep('freezed', new WorkflowTransitionContextDTO($entityWorkflow), 'to_freezed', new \DateTimeImmutable());
        $entityWorkflow->getCurrentStep()->setFreezeAfter(true);

        $this->assertTrue($entityWorkflow->isFreeze());

        $entityWorkflow->setStep('after_freeze', new WorkflowTransitionContextDTO($entityWorkflow), 'to_after_freeze', new \DateTimeImmutable());

        $this->assertTrue($entityWorkflow->isFreeze());

        $this->assertTrue($entityWorkflow->getCurrentStep()->isFreezeAfter());
    }

    public function testPreviousStepMetadataAreFilled()
    {
        $entityWorkflow = new EntityWorkflow();
        $initialStep = $entityWorkflow->getCurrentStep();

        $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_one', new \DateTimeImmutable('2024-01-01'), $user1 = new User());

        $previous = $entityWorkflow->getCurrentStep();

        $entityWorkflow->setStep('step_one', new WorkflowTransitionContextDTO($entityWorkflow), 'to_step_two', new \DateTimeImmutable('2024-01-02'), $user2 = new User());

        $final = $entityWorkflow->getCurrentStep();

        $stepsChained = $entityWorkflow->getStepsChained();

        self::assertCount(3, $stepsChained);
        self::assertSame($initialStep, $stepsChained[0]);
        self::assertSame($previous, $stepsChained[1]);
        self::assertSame($final, $stepsChained[2]);
        self::assertEquals($user1, $initialStep->getTransitionBy());
        self::assertEquals('2024-01-01', $initialStep->getTransitionAt()?->format('Y-m-d'));
        self::assertEquals('to_step_one', $initialStep->getTransitionAfter());
        self::assertEquals($user2, $previous->getTransitionBy());
        self::assertEquals('2024-01-02', $previous->getTransitionAt()?->format('Y-m-d'));
        self::assertEquals('to_step_two', $previous->getTransitionAfter());
    }

    public function testSetStepSignatureForUserIsCreated()
    {
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureUserSignature = $user = new User();

        $entityWorkflow->setStep('new', $dto, 'to_new', new \DateTimeImmutable());

        $actual = $entityWorkflow->getCurrentStep();

        self::assertCount(1, $actual->getSignatures());
        self::assertSame($user, $actual->getSignatures()->first()->getSigner());
    }

    public function testSetStepSignatureForPersonIsCreated()
    {
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = $person1 = new Person();
        $dto->futurePersonSignatures[] = $person2 = new Person();

        $entityWorkflow->setStep('new', $dto, 'to_new', new \DateTimeImmutable());

        $actual = $entityWorkflow->getCurrentStep();
        $persons = $actual->getSignatures()->map(fn (EntityWorkflowStepSignature $signature) => $signature->getSigner());

        self::assertCount(2, $actual->getSignatures());
        self::assertContains($person1, $persons);
        self::assertContains($person2, $persons);
    }

    public function testIsStaledAt(): void
    {
        $creationDate = new \DateTimeImmutable('2024-01-01');
        $firstStepDate = new \DateTimeImmutable('2024-01-02');
        $afterFistStep = new \DateTimeImmutable('2024-01-03');

        $entityWorkflow = new EntityWorkflow();

        self::assertFalse($entityWorkflow->isStaledAt($creationDate), 'an entityWorkflow with null createdAt date should never be staled at initial step');
        self::assertFalse($entityWorkflow->isStaledAt($firstStepDate), 'an entityWorkflow with null createdAt date should never be staled at initial step');
        self::assertFalse($entityWorkflow->isStaledAt($afterFistStep), 'an entityWorkflow with null createdAt date should never be staled at initial step');

        $entityWorkflow->setCreatedAt($creationDate);

        self::assertFalse($entityWorkflow->isStaledAt($creationDate), 'an entityWorkflow with no step after initial should be staled');
        self::assertTrue($entityWorkflow->isStaledAt($firstStepDate), 'an entityWorkflow with no step after initial should be staled');
        self::assertTrue($entityWorkflow->isStaledAt($afterFistStep), 'an entityWorkflow with no step after initial should be staled');

        // apply a first step
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('new_step', $dto, 'to_new_step', $firstStepDate);

        self::assertFalse($entityWorkflow->isStaledAt($creationDate));
        self::assertFalse($entityWorkflow->isStaledAt($firstStepDate));
        self::assertTrue($entityWorkflow->isStaledAt($afterFistStep));
    }
}
