<?php
/**
 * BookiFlex Notification Extension
 *
 * @package    BookiFlex
 * @subpackage Notification
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

 namespace BookiFlex\Extensions\Notification\Model;

use BookiFlex\Core\Model\Identifiable;
use BookiFlex\Core\Model\Reservation\Reservation;
use BookiFlex\Core\Model\Timestamps;
use BookiFlex\Core\Util\DateTimeUtils;

defined( 'ABSPATH' ) || exit;

class Notification
{
    use Timestamps;
    use Identifiable;

    public const MAX_ATTEMPTS = 3;

    private NotificationStatus $status;

    private int $attempt = 0;

    private array $details = [];

    public function __construct(
        private readonly string $sid,
        private readonly string $event, // e.g., 'new_reservation', 'pre_arrival'
        private readonly Recipient $stakeholder, // e.g., 'customer', 'manager'
        private readonly string $sendTo, // e.g., 'mail@mail.com', '+44 978 1111 222'
        private readonly string $transport,
        private ?\DateTimeImmutable $sendTime = null,
        private readonly ?Reservation $reservation = null,
    ) {
        $this->createdAt = DateTimeUtils::nowUtc();
        $this->sendTime  = $sendTime->setTimezone(new \DateTimeZone('UTC'));
        $this->status    = new NotificationStatus(NotificationStatus::PENDING);
    }

    public function getSid(): string
    {
        return $this->sid;
    }

    public function getEvent(): string
    {
        return $this->event;
    }

    /** Convenience: returns enum for known core events, null for custom */
    public function getNotificationEvent(): ?NotificationEvent
    {
        return NotificationEvent::tryFrom($this->event);
    }

    public function getStakeholder(): Recipient
    {
        return $this->stakeholder;
    }

    public function getSendTo(): string
    {
        return $this->sendTo;
    }

    /**
     * Datetime in UTC when least notification could be sent
     *
     * @param \DateTimeZone|null $timeZone
     * @return \DateTimeImmutable|null
     */
    public function getSendTime(?\DateTimeZone $timeZone = null): ?\DateTimeImmutable
    {
        if (!$timeZone) {
            $timeZone = new \DateTimeZone('UTC');
        }

        return $this->sendTime->setTimezone($timeZone);
    }

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

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

    public function isPending(): bool
    {
        return $this->status->isEquals(NotificationStatus::PENDING);
    }

    public function isSent(): bool
    {
        return $this->status->isEquals(NotificationStatus::SENT);
    }

    public function markAsSent(): void
    {
        $this->status->transitionTo(NotificationStatus::SENT);
    }

    public function markAsFailed(): void
    {
        $this->status->transitionTo(NotificationStatus::FAILED);
    }

    public function markAsCancelled(): void
    {
        $this->status->transitionTo(NotificationStatus::CANCELLED);
    }

    public function incAttempts(): int
    {
        $this->attempt++;
        return $this->attempt;
    }

    public function getAttempts(): int
    {
        return $this->attempt;
    }

    public function getDetails(): array
    {
        return $this->details;
    }

    public function setDetails(array $details): void
    {
        $this->details = $details;
    }

    public function hasSnapshot(): bool
    {
        return isset($this->details['snapshot']);
    }

    public function getSnapshot(): ?array
    {
        return $this->details['snapshot'] ?? null;
    }

    public function getReservation(): ?Reservation
    {
        return $this->reservation;
    }
}
