<?php

namespace FiloBlu\Esb\Model;

use DateTime;
use Exception;
use FiloBlu\Esb\Api\Data\QueueItemInterface;
use FiloBlu\Esb\Api\Data\StatusInterface;
use FiloBlu\Esb\Api\QueueItemRepositoryInterface;
use FiloBlu\Esb\Api\RetryStrategyInterface;

/**
 * Class CountToInfinityRetryStrategy
 * @package FiloBlu\Esb\Model
 */
class CountToInfinityRetryStrategy implements RetryStrategyInterface
{
    /**
     * @var QueueItemRepositoryInterface
     */
    private $queueItemRepository;
    /**
     * @var int
     */
    private $infinity;
    /**
     * @var int
     */
    private $timeBase;

    /**
     * CountToInfinityRetryStrategy constructor.
     * @param QueueItemRepositoryInterface $queueItemRepository
     * @param integer $timeBase time base in minutes
     * @param integer $infinity
     */
    public function __construct(QueueItemRepositoryInterface $queueItemRepository, $timeBase, $infinity)
    {
        $this->queueItemRepository = $queueItemRepository;
        $this->infinity = $infinity;
        $this->timeBase = $timeBase;
    }

    /**
     * Returns true if item is rescheduled false otherwise
     *
     * @param QueueItemInterface $queueItem
     * @return boolean
     * @throws Exception
     */
    public function reschedule(QueueItemInterface $queueItem): bool
    {
        $retryCount = $queueItem->getRetryCount() + 1;

        if ($retryCount >= $this->infinity) {
            return false;
        }

        $rescheduledQueueItem = clone $queueItem;
        $nextSchedule = clone $rescheduledQueueItem->getExecutedAt();
        $seconds = $this->timeBase * (2 ** ($retryCount)) * 60;
        $nextSchedule->setTimestamp($nextSchedule->getTimestamp() + $seconds);

        $status = clone $rescheduledQueueItem->getStatus();
        $rescheduledQueueItem
            ->setId(null)
            ->setRetryItemId($queueItem->getId())
            ->setRetryCount($retryCount)
            ->setStatus($status->setCode(StatusInterface::PENDING)->setInputData(null)->setOutputData(null))
            ->setExecutableAfter($nextSchedule)
            ->setPublishedAt(new DateTime())
            ->setExecutedAt(null)
            ->setFinishedAt(null);
        $this->queueItemRepository->save($rescheduledQueueItem);
        return true;
    }
}