<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Retry;

use Throwable;
use PpPhp\Core\Platform\Event\Queue\DeferredEvent;
use PpPhp\Core\Platform\Event\Queue\DeferredQueueManager;
use PpPhp\Core\Platform\Event\Retry\Contracts\RetryPolicyInterface;
use PpPhp\Core\Platform\Event\Retry\DeadLetter\DeadLetterQueue;
use PpPhp\Core\Platform\Event\Retry\DeadLetter\FailedEvent;
use PpPhp\Core\Platform\Event\Retry\Policy\ImmediateRetryPolicy;

final class RetryExecutor
{
    private RetryStatistics $statistics;

    public function __construct(
        private readonly DeferredQueueManager $queue = new DeferredQueueManager(),
        private readonly DeadLetterQueue $deadLetters = new DeadLetterQueue(),
        private readonly RetryPolicyInterface $policy = new ImmediateRetryPolicy()
    ) {
        $this->statistics = new RetryStatistics();
    }

    public function execute(DeferredEvent $event, callable $handler, int $maxAttempts = 3): RetryResult
    {
        $this->statistics = $this->statistics->incrementAttempted();

        try {
            $handler($event);
            $this->statistics = $this->statistics->incrementSucceeded();
            return RetryResult::success();
        } catch (Throwable $e) {
            $attempt = $event->attempts() + 1;
            $context = new RetryContext($event, $attempt, $maxAttempts, $e);

            if ($this->policy->shouldRetry($context)) {
                $retry = $event->incrementAttempts();
                $this->queue->driver()->enqueue($retry);
                $this->statistics = $this->statistics->incrementScheduled();
                return RetryResult::retry($this->policy->nextDelaySeconds($context), $e->getMessage());
            }

            $this->deadLetters->add(FailedEvent::fromDeferredEvent($event, $e->getMessage(), $attempt));
            $this->statistics = $this->statistics->incrementDeadLettered();
            return RetryResult::deadLetter($e->getMessage());
        }
    }

    public function queue(): DeferredQueueManager { return $this->queue; }
    public function deadLetters(): DeadLetterQueue { return $this->deadLetters; }
    public function statistics(): RetryStatistics { return $this->statistics; }
}
