<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Mailer\Model;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\User\Model\User;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

#[ORM\Entity(repositoryClass: 'CpCreation\VitiCore\Mailer\Repository\EmailLogRepository')]
#[ORM\Table(name: 'mailer_email_log')]
#[ORM\Index(name: 'mailer_email_log_user_idx', columns: ['user_id'])]
#[ORM\Index(name: 'mailer_email_log_cart_idx', columns: ['cart_id'])]
#[ORM\Index(name: 'mailer_email_log_status_idx', columns: ['status'])]
class EmailLog
{
    public const string STATUS_QUEUED = 'queued';
    public const string STATUS_SENT = 'sent';
    public const string STATUS_FAILED = 'failed';
    public const string STATUS_RETRYING = 'retrying';

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

    #[ORM\Column(type: Types::STRING, length: 80)]
    private string $type;

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

    #[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
    private ?string $recipientName = null;

    #[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
    private ?string $subject = null;

    #[ORM\Column(type: Types::STRING, length: 20)]
    private string $status = self::STATUS_QUEUED;

    #[ORM\Column(type: Types::STRING, length: 255, nullable: true)]
    private ?string $transport = null;

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

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

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

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

    #[ORM\ManyToOne(targetEntity: User::class)]
    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
    private ?User $user = null;

    #[ORM\ManyToOne(targetEntity: Cart::class)]
    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
    private ?Cart $cart = null;

    private function __construct(
        string $type,
        string $recipientEmail,
        ?string $recipientName,
        ?string $subject,
        ?string $transport,
        ?User $user,
        ?Cart $cart,
    ) {
        $this->id = Uuid::uuid4();
        $this->type = mb_substr($type, 0, 80);
        $this->recipientEmail = mb_substr($recipientEmail, 0, 255);
        $this->recipientName = null !== $recipientName ? mb_substr($recipientName, 0, 255) : null;
        $this->subject = null !== $subject ? mb_substr($subject, 0, 255) : null;
        $this->transport = null !== $transport ? mb_substr($transport, 0, 255) : null;
        $this->user = $user;
        $this->cart = $cart;
        $this->queuedAt = new \DateTimeImmutable();
    }

    public static function queued(
        string $type,
        string $recipientEmail,
        ?string $recipientName,
        ?string $subject,
        ?string $transport,
        ?User $user,
        ?Cart $cart,
    ): self {
        return new self($type, $recipientEmail, $recipientName, $subject, $transport, $user, $cart);
    }

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

    public function getType(): string
    {
        return $this->type;
    }

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

    public function getRecipientName(): ?string
    {
        return $this->recipientName;
    }

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

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

    public function getTransport(): ?string
    {
        return $this->transport;
    }

    public function getTransportLabel(): string
    {
        return self::labelForTransport($this->transport);
    }

    public static function labelForTransport(?string $transport): string
    {
        if (null === $transport || '' === trim($transport)) {
            return 'Transport inconnu';
        }

        $transport = trim($transport);
        if (\in_array($transport, ['main', '[main]'], true)) {
            return 'Transport principal';
        }

        $transport = preg_replace('#//[^/@]+@#', '//', $transport) ?? $transport;
        $scheme = parse_url($transport, \PHP_URL_SCHEME);
        $host = parse_url($transport, \PHP_URL_HOST);
        $port = parse_url($transport, \PHP_URL_PORT);

        if ('smtp' === $scheme || 'smtps' === $scheme) {
            return sprintf(
                'SMTP%s',
                \is_string($host) && '' !== $host ? sprintf(' (%s%s)', $host, \is_int($port) ? ':'.$port : '') : ''
            );
        }

        return $transport;
    }

    public function getError(): ?string
    {
        return $this->error;
    }

    public function getQueuedAt(): \DateTimeImmutable
    {
        return $this->queuedAt;
    }

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

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

    public function getUser(): ?User
    {
        return $this->user;
    }

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

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

    public function markFailed(string $error, bool $willRetry): void
    {
        $this->status = $willRetry ? self::STATUS_RETRYING : self::STATUS_FAILED;
        $this->failedAt = new \DateTimeImmutable();
        $this->error = mb_substr($error, 0, 2000);
    }
}
