<?php
/**
 * Save product result value object.
 *
 * Immutable value object returned by
 * {@see \Affilun\Application\Product\UseCase\SaveProductUseCase}
 * to convey the post-save record alongside the boolean
 * "was this an insert or an update?" signal the
 * `specifications/rest-api-spec.md` §8.4 response envelope
 * (`data.created`) is derived from.
 *
 * Field semantics match `rest-api-spec.md` §8.4 exactly:
 *
 *   - `record` is the canonical post-save
 *     {@see \Affilun\Infrastructure\Persistence\ProductRecord}
 *     returned by {@see \Affilun\Infrastructure\Persistence\ProductRepository::save()}.
 *     It always carries a non-null id (the §4.2 auto-increment
 *     primary key) and reflects any storage-side normalization the
 *     repository applied (timestamp truncation, default columns).
 *
 *   - `created` is true when the save inserted a fresh row
 *     (§8.4 "新規作成時は 201 + `data.created=true`") and false
 *     when the save updated an existing
 *     `(provider, provider_item_id)` pair (§8.4 "既存リソースを
 *     更新する。`data.created=false`"). The REST mapper consults
 *     this flag to choose between 201 and 200 status codes; the
 *     UseCase carries the bit so the controller does not have to
 *     re-derive it from the persistence layer.
 *
 * Design notes:
 *
 *   - The result deliberately does NOT carry the originating
 *     {@see \Affilun\Domain\Product\ProductSnapshot}.
 *     The post-save `record` is the canonical shape every
 *     downstream consumer (REST envelope, audit log) needs;
 *     retaining the snapshot would invite drift between the
 *     normalized record and the upstream-supplied snapshot
 *     fields.
 *
 *   - The class is final because the REST mapping layer pattern-
 *     matches on the exact type. Future variants (for example a
 *     "no-op upsert" outcome where the canonical hash matched and
 *     no row was rewritten) should be modeled as a sibling type
 *     rather than a subclass.
 *
 * @package Affilun\Application\Product\UseCase
 */

declare(strict_types=1);

namespace Affilun\Application\Product\UseCase;

use Affilun\Infrastructure\Persistence\ProductRecord;

/**
 * Immutable result envelope for {@see SaveProductUseCase::execute()}.
 *
 * See the file-level docblock for the §8.4 mapping that the
 * `created` flag drives and the rationale for not carrying the
 * originating {@see \Affilun\Domain\Product\ProductSnapshot}.
 */
final class SaveProductResult {

	/**
	 * Canonical post-save record.
	 */
	private ProductRecord $record;

	/**
	 * Whether the save inserted a fresh row (true) or updated an
	 * existing row (false).
	 */
	private bool $created;

	/**
	 * Construct a save result with explicit values for every
	 * field.
	 *
	 * @param ProductRecord $record  Canonical post-save record.
	 * @param bool          $created True on insert, false on update.
	 */
	public function __construct( ProductRecord $record, bool $created ) {
		$this->record  = $record;
		$this->created = $created;
	}

	/**
	 * Canonical post-save record.
	 */
	public function record(): ProductRecord {
		return $this->record;
	}

	/**
	 * Whether the save inserted a fresh row.
	 */
	public function created(): bool {
		return $this->created;
	}

	/**
	 * Deep equality against another result.
	 *
	 * Records are compared via {@see ProductRecord::toArray()} so
	 * two records carrying the same column payload compare equal
	 * even when they are distinct object instances.
	 *
	 * @param self $other Result to compare.
	 */
	public function equals( self $other ): bool {
		if ( $this->created !== $other->created ) {
			return false;
		}

		return $this->record->toArray() === $other->record->toArray();
	}
}
