<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Marketing\Model;

use CpCreation\VitiCore\Behavior\HasTimestamp;
use CpCreation\VitiCore\Behavior\Impl\Timestamp;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Marketing\Repository\ReviewInvitationRepository;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

#[ORM\Entity(repositoryClass: ReviewInvitationRepository::class)]
#[ORM\Table(name: 'marketing_review_invitation')]
#[ORM\UniqueConstraint(name: 'uniq_marketing_review_invitation_cart', columns: ['cart_id'])]
#[ORM\UniqueConstraint(name: 'uniq_marketing_review_invitation_token_hash', columns: ['token_hash'])]
#[ORM\UniqueConstraint(name: 'uniq_marketing_review_invitation_reminder_token_hash', columns: ['reminder_token_hash'])]
#[ORM\Index(name: 'marketing_review_invitation_status_idx', columns: ['status'])]
#[ORM\Index(name: 'marketing_review_invitation_reminder_due_idx', columns: ['status', 'reminder_sent_at', 'reminder_suppressed_at', 'sent_at'])]
#[ORM\Index(name: 'marketing_review_invitation_personal_data_purge_idx', columns: ['personal_data_purged_at', 'expires_at'])]
#[ORM\HasLifecycleCallbacks]
class ReviewInvitation implements HasTimestamp
{
    use Timestamp;

    public const string STATUS_SCHEDULED = 'scheduled';
    public const string STATUS_SENT = 'sent';
    public const string STATUS_SUBMITTED = 'submitted';
    public const string STATUS_EXPIRED = 'expired';
    public const string STATUS_CANCELED = 'canceled';

    #[ORM\Id]
    #[ORM\Column(type: 'uuid')]
    #[ORM\GeneratedValue(strategy: 'NONE')]
    private UuidInterface $id;

    #[ORM\OneToOne(targetEntity: Cart::class)]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    private Cart $cart;

    #[ORM\Column(type: 'string', length: 255)]
    private string $recipientEmail;

    #[ORM\Column(type: 'string', length: 64)]
    private string $tokenHash;

    #[ORM\Column(type: 'string', length: 24)]
    private string $status = self::STATUS_SCHEDULED;

    #[ORM\Column(type: 'datetime_immutable')]
    private \DateTimeImmutable $scheduledAt;

    #[ORM\Column(type: 'datetime_immutable')]
    private \DateTimeImmutable $expiresAt;

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

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

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

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

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

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

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

    public function __construct(
        Cart $cart,
        string $recipientEmail,
        string $tokenHash,
        \DateTimeImmutable $scheduledAt,
        \DateTimeImmutable $expiresAt,
    ) {
        $this->id = Uuid::uuid4();
        $this->cart = $cart;
        $this->recipientEmail = mb_strtolower(mb_trim($recipientEmail));
        $this->tokenHash = $tokenHash;
        $this->scheduledAt = $scheduledAt;
        $this->expiresAt = $expiresAt;
    }

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

    public function getCart(): Cart
    {
        return $this->cart;
    }

    public function getRecipientEmail(): string
    {
        return $this->recipientEmail;
    }

    public function getTokenHash(): string
    {
        return $this->tokenHash;
    }

    public function getStatus(): string
    {
        return $this->status;
    }

    public function getScheduledAt(): \DateTimeImmutable
    {
        return $this->scheduledAt;
    }

    public function getExpiresAt(): \DateTimeImmutable
    {
        return $this->expiresAt;
    }

    public function getSentAt(): ?\DateTimeImmutable
    {
        return $this->sentAt;
    }

    public function getReminderTokenHash(): ?string
    {
        return $this->reminderTokenHash;
    }

    public function getReminderQueuedAt(): ?\DateTimeImmutable
    {
        return $this->reminderQueuedAt;
    }

    public function getReminderSentAt(): ?\DateTimeImmutable
    {
        return $this->reminderSentAt;
    }

    public function getReminderSuppressedAt(): ?\DateTimeImmutable
    {
        return $this->reminderSuppressedAt;
    }

    public function getSubmittedAt(): ?\DateTimeImmutable
    {
        return $this->submittedAt;
    }

    public function getPersonalDataPurgedAt(): ?\DateTimeImmutable
    {
        return $this->personalDataPurgedAt;
    }

    public function isUsableAt(\DateTimeImmutable $now): bool
    {
        return \in_array($this->status, [self::STATUS_SENT, self::STATUS_SUBMITTED], true)
            && $this->expiresAt > $now;
    }

    public function markSent(?\DateTimeImmutable $sentAt = null): void
    {
        $this->status = self::STATUS_SENT;
        $this->sentAt = $sentAt ?? new \DateTimeImmutable();
    }

    public function canReceiveSecondReminderAt(\DateTimeImmutable $now): bool
    {
        return self::STATUS_SENT === $this->status
            && null !== $this->sentAt
            && null === $this->reminderSentAt
            && null === $this->reminderSuppressedAt
            && $this->expiresAt > $now;
    }

    public function scheduleReminder(string $tokenHash, ?\DateTimeImmutable $queuedAt = null): bool
    {
        $queuedAt ??= new \DateTimeImmutable();
        if (!$this->canReceiveSecondReminderAt($queuedAt)) {
            return false;
        }

        $this->reminderTokenHash = $tokenHash;
        $this->reminderQueuedAt = $queuedAt;

        return true;
    }

    public function markReminderSent(?\DateTimeImmutable $sentAt = null): void
    {
        $this->reminderSentAt ??= $sentAt ?? new \DateTimeImmutable();
    }

    public function suppressReminder(?\DateTimeImmutable $suppressedAt = null): void
    {
        $this->reminderSuppressedAt ??= $suppressedAt ?? new \DateTimeImmutable();
    }

    public function markSubmitted(?\DateTimeImmutable $submittedAt = null): void
    {
        $this->status = self::STATUS_SUBMITTED;
        $this->submittedAt ??= $submittedAt ?? new \DateTimeImmutable();
    }

    public function markExpired(): void
    {
        $this->status = self::STATUS_EXPIRED;
    }

    public function cancel(): void
    {
        $this->status = self::STATUS_CANCELED;
    }
}
