<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity]
class ConsentForm
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\Column(type: UuidType::NAME, unique: true)]
    #[ORM\CustomIdGenerator(class: UuidGenerator::class)]
    private Uuid $id;

    #[ORM\ManyToOne(targetEntity: OfferInterface::class, inversedBy: 'consentForms')]
    #[ORM\JoinColumn(name: 'offer_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
    private ?OfferInterface $offer = null;

    #[ORM\ManyToOne(targetEntity: Edition::class, inversedBy: 'consentForms')]
    #[ORM\JoinColumn(name: 'edition_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
    private ?Edition $edition = null;

    #[ORM\OneToOne(targetEntity: ConsentText::class, cascade: ['persist'])]
    #[ORM\JoinColumn(name: 'consent_text', referencedColumnName: 'id', onDelete: 'SET NULL')]
    private ?ConsentText $consentText = null;

    #[ORM\Column(type: 'string', length: 12, options: ['default' => 'participant'])]
    private string $subject = 'participant';

    #[ORM\Column(type: 'string', length: 16, options: ['default' => 'compulsory'])]
    private string $compulsory = 'compulsory';

    #[ORM\Column(type: 'string', nullable: true)]
    private ?string $title = null;

    #[ORM\Column(type: 'datetime_immutable', options: ['default' => 'CURRENT_TIMESTAMP'])]
    private \DateTimeInterface $createdAt;

    #[ORM\OneToMany(mappedBy: 'consentForm', targetEntity: Consent::class)]
    #[ORM\OrderBy(['createdAt' => 'DESC'])]
    #[ORM\JoinColumn(referencedColumnName: 'id', onDelete: 'SET NULL')]
    private Collection $signatures;

    public function __construct(?Uuid $uuid = null)
    {
        if ($uuid instanceof Uuid) {
            $this->id = $uuid;
        }

        $this->createdAt = new \DateTimeImmutable();
        $this->signatures = new ArrayCollection();
    }

    public function getId(): Uuid
    {
        return $this->id;
    }

    public function getCreatedAt(): \DateTimeInterface
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeInterface $createdAt): void
    {
        $this->createdAt = $createdAt;
    }

    public function getOffer(): ?OfferInterface
    {
        return $this->offer;
    }

    public function setOffer(?OfferInterface $offer): void
    {
        $this->offer = $offer;
    }

    public function getEdition(): ?Edition
    {
        return $this->edition;
    }

    public function setEdition(?Edition $edition): void
    {
        $this->edition = $edition;
    }

    public function getConsentText(): ?ConsentText
    {
        return $this->consentText;
    }

    public function setConsentText(?ConsentText $consentText): void
    {
        $this->consentText = $consentText;
    }

    public function getText(): ?string
    {
        return $this->consentText?->getText();
    }

    public function setText(?string $text): void
    {
        if (null === $text) {
            $this->consentText = null;

            return;
        }

        if ($this->consentText?->getText() === $text) {
            return;
        }

        $this->consentText = new ConsentText($text);
    }

    /**
     * Whether this consent is collected in the prefaced "step 1" gate (before
     * participant selection) rather than inline under the selected participant.
     * That is a PLACEMENT decision, driven by scope: account-subject consents
     * and edition-scoped consents are gathered up front; offer-scoped
     * participant consents render inline with the participant.
     *
     * Note this is independent of CARDINALITY (how often the consent is asked):
     * that is driven by {@see isPerParticipant()} — an edition-scoped
     * participant consent is shown in the gate but once PER participant.
     */
    public function isUserLevel(): bool
    {
        return 'account' === $this->subject || (null === $this->offer && 'participant' === $this->subject);
    }

    /**
     * Whether this consent is collected once per participant (subject =
     * participant) rather than once for the whole account (subject = account).
     * Orthogonal to {@see isUserLevel()}, which only decides gate vs inline
     * placement.
     */
    public function isPerParticipant(): bool
    {
        return 'participant' === $this->subject;
    }

    /**
     * The "Einverständnis der Veranstaltenden": a season-wide declaration a host
     * (Veranstalter) signs once. Distinguished by subject so it is filtered out
     * of the participant/account consent flows ({@see Edition::getConsentForms()}
     * and {@see \Ferienpass\CoreBundle\Repository\ConsentFormRepository}).
     */
    public function isHost(): bool
    {
        return 'host' === $this->subject;
    }

    public function getSubject(): string
    {
        return $this->subject;
    }

    public function setSubject(string $subject): void
    {
        $this->subject = $subject;
    }

    public function getCompulsory(): string
    {
        return $this->compulsory;
    }

    public function setCompulsory(?string $compulsory): void
    {
        if (null === $compulsory) {
            return;
        }

        $this->compulsory = $compulsory;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(?string $title): void
    {
        $this->title = $title;
    }

    public function getSignatures(): Collection
    {
        return $this->signatures;
    }
}
