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

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\Validator\TransitionHasDestineeIfIsSentExternal;
use Chill\MainBundle\Workflow\Validator\TransitionHasDestUserIfRequired;
use Chill\MainBundle\Workflow\Validator\TransitionHasSignerIfSignature;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Validator\ThirdPartyHasEmail;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Workflow\Transition;

/**
 * Context for a transition on an workflow entity.
 */
#[TransitionHasDestineeIfIsSentExternal]
#[TransitionHasDestUserIfRequired]
#[TransitionHasSignerIfSignature]
class WorkflowTransitionContextDTO
{
    /**
     * a list of future dest users or user groups for the next step.
     *
     * This is in used in order to let controller inform who will be the future users which will validate
     * the next step. This is necessary to perform some computation about the next users, before they are
     * associated to the entity EntityWorkflowStep.
     *
     * @var list<User|UserGroup>
     */
    public array $futureDestUsers = [];

    /**
     * a list of future cc users for the next step.
     *
     * @var array|User[]
     */
    public array $futureCcUsers = [];

    /**
     * A list of future @see{Person} with will sign the next step.
     *
     * @var list<Person>
     */
    public array $futurePersonSignatures = [];

    /**
     * An eventual user which is requested to apply a signature.
     */
    public ?User $futureUserSignature = null;

    /**
     * a list of future destinee third parties, when a workflow does send the document
     * to a remote third party.
     *
     * @var array<ThirdParty>
     */
    #[Assert\All(
        new ThirdPartyHasEmail(),
    )]
    public array $futureDestineeThirdParties = [];

    /**
     * a list of future destinee emails, when a workflow does send the document to a remote
     * email.
     *
     * @var array<string>
     */
    #[Assert\All([
        new Assert\NotBlank(),
        new Assert\Email(),
    ])]
    public array $futureDestineeEmails = [];

    #[Assert\NotNull]
    public ?Transition $transition = null;

    public string $comment = '';

    public function __construct(
        public EntityWorkflow $entityWorkflow,
    ) {}

    /**
     * @return list<User>
     */
    public function getFutureDestUsers(): array
    {
        return array_values(array_filter($this->futureDestUsers, fn (User|UserGroup $user) => $user instanceof User));
    }

    /**
     * @return list<UserGroup>
     */
    public function getFutureDestUserGroups(): array
    {
        return array_values(array_filter($this->futureDestUsers, fn (User|UserGroup $user) => $user instanceof UserGroup));
    }

    #[Assert\Callback()]
    public function validateCCUserIsNotInDest(ExecutionContextInterface $context, $payload): void
    {
        foreach ($this->futureDestUsers as $u) {
            if (in_array($u, $this->futureCcUsers, true)) {
                $context
                    ->buildViolation('workflow.The user in cc cannot be a dest user in the same workflow step')
                    ->atPath('ccUsers')
                    ->addViolation();
            }
        }
    }
}
