<?php
/**
 * Sync-job not-found Application-layer exception.
 *
 * Phase 11 slice 11e-A exception raised by
 * {@see \Affilun\Application\SyncJobs\UseCase\GetSyncJobUseCase}
 * when the supplied id does not match any row currently stored in
 * the `affilun_sync_jobs` table.
 *
 * Design notes:
 *
 *   - Extends {@see \RuntimeException} because "not found" is a
 *     runtime outcome of a controller-supplied id, not a programming
 *     error at the call site. The REST controller in a later slice
 *     translates this into the `affilun_sync_job_not_found` error code
 *     enumerated in `specifications/rest-api-spec.md` §15.2.
 *
 *   - Carries the offending id so the controller can stamp it back
 *     into the response envelope without re-parsing the message.
 *
 *   - Final because consumers pattern-match on the exact type when
 *     deciding the 404 mapping; consumers that need additional
 *     context should compose rather than subclass.
 *
 * @package Affilun\Application\SyncJobs\Exception
 */

declare(strict_types=1);

namespace Affilun\Application\SyncJobs\Exception;

use RuntimeException;
use Throwable;

/**
 * Application-layer exception signalling the supplied sync-job id
 * could not be resolved to a stored {@see \Affilun\Infrastructure\Persistence\SyncJobRecord}.
 *
 * See the file-level docblock for the rationale for inheriting from
 * {@see RuntimeException} and the mapping to the §15.2
 * `affilun_sync_job_not_found` REST error code.
 */
final class SyncJobNotFoundException extends RuntimeException {

	/**
	 * Sync-job id that failed to resolve.
	 */
	private int $id;

	/**
	 * Construct a not-found exception with explicit values for every
	 * cross-cutting field.
	 *
	 * @param string         $message  Human-readable summary.
	 * @param int            $id       Sync-job id that failed to resolve.
	 * @param Throwable|null $previous Underlying cause, or null.
	 */
	public function __construct( string $message, int $id, ?Throwable $previous = null ) {
		parent::__construct( $message, 0, $previous );
		$this->id = $id;
	}

	/**
	 * Sync-job id that failed to resolve.
	 */
	public function id(): int {
		return $this->id;
	}

	/**
	 * Convenience factory for the "no row matches this id" case.
	 *
	 * @param int $id Sync-job id that failed to resolve.
	 */
	public static function forId( int $id ): self {
		return new self(
			sprintf( 'Sync job not found: %d.', $id ),
			$id
		);
	}
}
