<?php

declare(strict_types=1);

namespace App\Framework\Retry\Strategies;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Retry\RetryStrategy;
use Throwable;

/**
 * Linear Delay Retry-Strategie
 *
 * Konstante Wartezeit zwischen Versuchen: 500ms -> 500ms -> 500ms
 * Gut für Services mit vorhersagbaren Recovery-Zeiten
 */
final readonly class LinearDelayStrategy implements RetryStrategy
{
    public function __construct(
        private int $maxAttempts = 3,
        private Duration $delay = new Duration(500),
        private array $retryableExceptions = [
            \RuntimeException::class,
            \Exception::class,
        ]
    ) {
    }

    public function shouldRetry(int $currentAttempt, Throwable $exception): bool
    {
        if ($currentAttempt >= $this->maxAttempts) {
            return false;
        }

        return $this->isRetryableException($exception);
    }

    public function getDelay(int $currentAttempt, Throwable $exception): Duration
    {
        if ($currentAttempt === 0) {
            return Duration::zero();
        }

        return $this->delay;
    }

    public function getMaxAttempts(): int
    {
        return $this->maxAttempts;
    }

    public function isRetryableException(Throwable $exception): bool
    {
        foreach ($this->retryableExceptions as $retryableClass) {
            if ($exception instanceof $retryableClass) {
                return true;
            }
        }

        return false;
    }

    /**
     * Factory Methods
     */
    public static function fast(int $maxAttempts = 3): self
    {
        return new self(
            maxAttempts: $maxAttempts,
            delay: Duration::fromMilliseconds(100)
        );
    }

    public static function medium(int $maxAttempts = 3): self
    {
        return new self(
            maxAttempts: $maxAttempts,
            delay: Duration::fromMilliseconds(500)
        );
    }

    public static function slow(int $maxAttempts = 3): self
    {
        return new self(
            maxAttempts: $maxAttempts,
            delay: Duration::fromSeconds(2)
        );
    }
}
