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

use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait;
use Chill\MainBundle\Entity\HasCenterInterface;
use Chill\MainBundle\Entity\HasScopeInterface;
use Chill\MainBundle\Entity\Scope;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

/**
 * Class Participation.
 */
#[UniqueEntity(['event', 'person'], message: 'event.validation.person_already_participate_to_event')]
#[ORM\Entity(repositoryClass: \Chill\EventBundle\Repository\ParticipationRepository::class)]
#[ORM\UniqueConstraint(name: 'chill_event_participation_event_person_unique_idx', columns: ['event_id', 'person_id'])] // @ORM\HasLifecycleCallbacks
#[ORM\Table(name: 'chill_event_participation')]
#[ORM\UniqueConstraint(name: 'chill_event_participation_event_person_unique_idx', columns: ['event_id', 'person_id'])]
#[ORM\UniqueConstraint(name: 'chill_event_participation_event_person_unique_idx', columns: ['event_id', 'person_id'])]
#[ORM\HasLifecycleCallbacks] // @ORM\HasLifecycleCallbacks
class Participation implements \ArrayAccess, HasCenterInterface, HasScopeInterface, TrackUpdateInterface, TrackCreationInterface
{
    use TrackCreationTrait;
    use TrackUpdateTrait;

    #[ORM\ManyToOne(targetEntity: Event::class, inversedBy: 'participations')]
    private ?Event $event = null;

    #[ORM\Id]
    #[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    private ?int $id = null;

    #[Assert\NotNull]
    #[ORM\ManyToOne(targetEntity: Person::class)]
    private ?Person $person = null;

    #[ORM\ManyToOne(targetEntity: Role::class)]
    private ?Role $role = null;

    #[Assert\NotNull]
    #[ORM\ManyToOne(targetEntity: Status::class)]
    private ?Status $status = null;

    public function getCenter()
    {
        if (null === $this->getEvent()) {
            throw new \RuntimeException('The event is not linked with this instance. You should initialize the event with a valid center before.');
        }

        return $this->getEvent()->getCenter();
    }

    /**
     * Get event.
     */
    public function getEvent(): ?Event
    {
        return $this->event;
    }

    /**
     * Get id.
     */
    public function getId(): int
    {
        return $this->id;
    }

    /**
     * Get lastUpdate.
     *
     * @return \DateTimeInterface|null
     */
    public function getLastUpdate()
    {
        return $this->getUpdatedAt();
    }

    /**
     * Get person.
     *
     * @return Person
     */
    public function getPerson()
    {
        return $this->person;
    }

    /**
     * Get role.
     */
    public function getRole(): ?Role
    {
        return $this->role;
    }

    /**
     * @return Scope
     */
    public function getScope()
    {
        if (null === $this->getEvent()) {
            throw new \RuntimeException('The event is not linked with this instance. You should initialize the event with a valid center before.');
        }

        return $this->getEvent()->getCircle();
    }

    /**
     * Get status.
     */
    public function getStatus(): ?Status
    {
        return $this->status;
    }

    /**
     * Check that :.
     *
     * - the role can be associated with this event type
     * - the status can be associated with this event type
     */
    public function isConsistent(ExecutionContextInterface $context)
    {
        if (null === $this->getEvent() || null === $this->getRole() || null === $this->getStatus()) {
            return;
        }

        if (
            $this->getRole()->getType()->getId() !==
                $this->getEvent()->getType()->getId()
        ) {
            $context->buildViolation('The role is not allowed with this event type')
                ->atPath('role')
                ->addViolation();
        }

        if (
            $this->getStatus()->getType()->getId() !==
                $this->getEvent()->getType()->getId()
        ) {
            $context->buildViolation('The status is not allowed with this event type')
                ->atPath('status')
                ->addViolation();
        }
    }

    public function offsetExists(mixed $offset): bool
    {
        return \in_array($offset, [
            'person', 'role', 'status', 'event',
        ], true);
    }

    /**
     * @return Event|Person|Role|Status
     */
    public function offsetGet(mixed $offset): mixed
    {
        return match ($offset) {
            'person' => $this->getPerson(),
            'role' => $this->getRole(),
            'status' => $this->getStatus(),
            'event' => $this->getEvent(),
            default => throw new \LogicException('this offset does not exists : '.$offset),
        };
    }

    public function offsetSet(mixed $offset, mixed $value): void
    {
        switch ($offset) {
            case 'person':
                $this->setPerson($value);

                break;

            case 'role':
                $this->setRole($value);

                break;

            case 'status':
                $this->setStatus($value);

                break;

            case 'event':
                $this->setEvent($value);

                break;
        }
    }

    public function offsetUnset(mixed $offset): void
    {
        $this->offsetSet($offset, null);
    }

    /**
     * Set event.
     *
     * @return Participation
     */
    public function setEvent(?Event $event = null)
    {
        $this->event = $event;

        return $this;
    }

    /**
     * Set person.
     *
     * @return Participation
     */
    public function setPerson(?Person $person = null)
    {
        $this->person = $person;

        return $this;
    }

    /**
     * Set role.
     *
     * @return Participation
     */
    public function setRole(?Role $role = null)
    {
        $this->role = $role;

        return $this;
    }

    /**
     * Set status.
     *
     * @return Participation
     */
    public function setStatus(?Status $status = null)
    {
        $this->status = $status;

        return $this;
    }

    /**
     * Set lastUpdate.
     *
     * @return Participation
     *
     * @deprecated
     */
    protected function update()
    {
        return $this;
    }
}
