<?php
/**
 * Scheduled link-health tick result.
 *
 * @package Affilun\Application\LinkHealth
 */

declare(strict_types=1);

namespace Affilun\Application\LinkHealth;

use DateTimeImmutable;

/**
 * Summary returned by one scheduled link-health batch tick.
 */
final class ScheduledLinkHealthTickResult {

	/**
	 * @param string|null            $skippedReason   Skip reason, or null.
	 * @param int                    $scannedProducts Product rows scanned.
	 * @param int                    $submittedUrls   URLs submitted to Cloud.
	 * @param int                    $persistedResults Product statuses persisted.
	 * @param int                    $attempts        Stored retry attempts.
	 * @param DateTimeImmutable|null $nextAvailableAt Earliest retry instant.
	 */
	private function __construct(
		private ?string $skippedReason,
		private int $scannedProducts,
		private int $submittedUrls,
		private int $persistedResults,
		private int $attempts,
		private ?DateTimeImmutable $nextAvailableAt
	) {}

	/**
	 * Build skipped result.
	 *
	 * @param string                 $reason          Skip reason.
	 * @param int                    $attempts        Stored retry attempts.
	 * @param DateTimeImmutable|null $nextAvailableAt Earliest retry instant.
	 */
	public static function skipped(
		string $reason,
		int $attempts = 0,
		?DateTimeImmutable $nextAvailableAt = null
	): self {
		return new self( $reason, 0, 0, 0, $attempts, $nextAvailableAt );
	}

	/**
	 * Build successful result.
	 *
	 * @param int $scannedProducts  Product rows scanned.
	 * @param int $submittedUrls    URLs submitted to Cloud.
	 * @param int $persistedResults Product statuses persisted.
	 */
	public static function success( int $scannedProducts, int $submittedUrls, int $persistedResults ): self {
		return new self( null, $scannedProducts, $submittedUrls, $persistedResults, 0, null );
	}

	/**
	 * Build retry-scheduled result.
	 *
	 * @param int               $attempts        Stored retry attempts.
	 * @param DateTimeImmutable $nextAvailableAt Earliest retry instant.
	 */
	public static function retryScheduled( int $attempts, DateTimeImmutable $nextAvailableAt ): self {
		return new self( null, 0, 0, 0, $attempts, $nextAvailableAt );
	}

	/** Skip reason, or null. */
	public function skippedReason(): ?string {
		return $this->skippedReason;
	}

	/** Product rows scanned. */
	public function scannedProducts(): int {
		return $this->scannedProducts;
	}

	/** URLs submitted to Cloud. */
	public function submittedUrls(): int {
		return $this->submittedUrls;
	}

	/** Product statuses persisted. */
	public function persistedResults(): int {
		return $this->persistedResults;
	}

	/** Stored retry attempts. */
	public function attempts(): int {
		return $this->attempts;
	}

	/** Earliest retry instant. */
	public function nextAvailableAt(): ?DateTimeImmutable {
		return $this->nextAvailableAt;
	}

	/** Whether the tick was skipped. */
	public function isSkipped(): bool {
		return $this->skippedReason !== null;
	}

	/** Whether retry/backoff was scheduled. */
	public function isRetryScheduled(): bool {
		return $this->skippedReason === null && $this->nextAvailableAt !== null;
	}

	/** Whether the tick completed successfully. */
	public function isSuccess(): bool {
		return $this->skippedReason === null && $this->nextAvailableAt === null;
	}
}
