<?php
/**
 * Meta-flags value object.
 *
 * Immutable value object that carries the §8.2 `meta` slot toggles
 * a §8.2 ProductCardViewModel exposes to its template. The type
 * collects the four boolean axes the §10.x display rules branch on
 * — show brand, show description, show price, show provider badge —
 * into a single validated value so renderers and templates do not
 * have to chase the same flags across an EmbedConfig, a
 * RenderContext, and a ProviderCapabilities map at render time.
 *
 * Field semantics match `specifications/rendering-and-template-spec.md`
 * §8.2 / §10.2 / §10.3 / §10.5 exactly:
 *
 *   - `showBrand` is the §10.2 brand-visibility toggle. When false
 *     the renderer suppresses the brand region entirely; when true
 *     the renderer emits the §10.2 brand label keyed off the
 *     ProductInfoView `brand` field.
 *
 *   - `showDescription` is the §10.3 description-visibility
 *     toggle. When false the renderer suppresses the description
 *     region; when true the renderer emits the §10.3 short
 *     description keyed off the ProductInfoView `description`
 *     field.
 *
 *   - `showPrice` is the §10.5 price-visibility toggle. When false
 *     the renderer suppresses the price region even when the
 *     PriceView itself reports `visible = true`; when true the
 *     renderer defers to the PriceView's own visibility flag.
 *     This axis exists so that an embed configuration can opt out
 *     of price display at the template-meta layer without rebuilding
 *     the underlying PriceView.
 *
 *   - `showProviderBadge` is the §8.2 provider-badge-visibility
 *     toggle. When false the renderer suppresses the provider
 *     badge entry from the `badges` array; when true the renderer
 *     emits a {@see BadgeView} with `kind = 'provider'`.
 *
 * The canonical "frontend defaults" branch is exposed as a static
 * factory {@see self::defaults()} so call sites do not have to
 * spell out the four canonical default values to construct the
 * baseline view. Per-axis adjustments are exposed through
 * {@see self::withShowBrand()}, {@see self::withShowDescription()},
 * {@see self::withShowPrice()}, and {@see self::withShowProviderBadge()}
 * so a ViewModel factory can flip a single flag without re-spelling
 * every other field.
 *
 * Instances are immutable.
 *
 * @package CommerceLinkBuilder\Application\Render\ViewModel\Parts
 */

declare(strict_types=1);

namespace CommerceLinkBuilder\Application\Render\ViewModel\Parts;

/**
 * Immutable meta-flags view used by §8.2 ProductCardViewModel and
 * the §10.2 / §10.3 / §10.5 visibility rules.
 *
 * See the file-level docblock for the mapping to
 * `rendering-and-template-spec.md` §8.2 / §10.x and the rationale
 * for the per-axis wither helpers.
 */
final class MetaFlags {

	/**
	 * Whether the §10.2 brand region renders.
	 */
	private bool $showBrand;

	/**
	 * Whether the §10.3 description region renders.
	 */
	private bool $showDescription;

	/**
	 * Whether the §10.5 price region renders.
	 */
	private bool $showPrice;

	/**
	 * Whether the §8.2 provider badge entry renders.
	 */
	private bool $showProviderBadge;

	/**
	 * Construct a meta-flags view with explicit values for every
	 * axis.
	 *
	 * No validation is performed beyond PHP's native bool typing;
	 * every combination is meaningful.
	 *
	 * @param bool $showBrand         Whether the §10.2 brand region renders.
	 * @param bool $showDescription   Whether the §10.3 description region renders.
	 * @param bool $showPrice         Whether the §10.5 price region renders.
	 * @param bool $showProviderBadge Whether the §8.2 provider badge entry renders.
	 */
	public function __construct(
		bool $showBrand,
		bool $showDescription,
		bool $showPrice,
		bool $showProviderBadge
	) {
		$this->showBrand         = $showBrand;
		$this->showDescription   = $showDescription;
		$this->showPrice         = $showPrice;
		$this->showProviderBadge = $showProviderBadge;
	}

	/**
	 * Construct a meta-flags view with the canonical "frontend
	 * defaults" combination.
	 *
	 * Convenience factory for the §8.2 baseline so call sites do
	 * not have to spell out the four canonical default values.
	 * Returns a view with `showBrand = true`,
	 * `showDescription = true`, `showPrice = true`, and
	 * `showProviderBadge = false` — the §8.2 example structure's
	 * `meta` slot, verbatim.
	 */
	public static function defaults(): self {
		return new self( true, true, true, false );
	}

	/**
	 * Whether the §10.2 brand region renders.
	 */
	public function showBrand(): bool {
		return $this->showBrand;
	}

	/**
	 * Whether the §10.3 description region renders.
	 */
	public function showDescription(): bool {
		return $this->showDescription;
	}

	/**
	 * Whether the §10.5 price region renders.
	 */
	public function showPrice(): bool {
		return $this->showPrice;
	}

	/**
	 * Whether the §8.2 provider badge entry renders.
	 */
	public function showProviderBadge(): bool {
		return $this->showProviderBadge;
	}

	/**
	 * Produce a copy of this view with `showBrand` replaced.
	 *
	 * @param bool $showBrand New brand-visibility flag.
	 */
	public function withShowBrand( bool $showBrand ): self {
		$clone            = clone $this;
		$clone->showBrand = $showBrand;
		return $clone;
	}

	/**
	 * Produce a copy of this view with `showDescription` replaced.
	 *
	 * @param bool $showDescription New description-visibility flag.
	 */
	public function withShowDescription( bool $showDescription ): self {
		$clone                  = clone $this;
		$clone->showDescription = $showDescription;
		return $clone;
	}

	/**
	 * Produce a copy of this view with `showPrice` replaced.
	 *
	 * @param bool $showPrice New price-visibility flag.
	 */
	public function withShowPrice( bool $showPrice ): self {
		$clone            = clone $this;
		$clone->showPrice = $showPrice;
		return $clone;
	}

	/**
	 * Produce a copy of this view with `showProviderBadge`
	 * replaced.
	 *
	 * @param bool $showProviderBadge New provider-badge-visibility flag.
	 */
	public function withShowProviderBadge( bool $showProviderBadge ): self {
		$clone                    = clone $this;
		$clone->showProviderBadge = $showProviderBadge;
		return $clone;
	}

	/**
	 * Report whether this view equals the supplied one.
	 *
	 * Equality is structural: every flag must match with strict
	 * comparison.
	 *
	 * @param self $other Other view to compare against.
	 */
	public function equals( self $other ): bool {
		return $this->showBrand === $other->showBrand
			&& $this->showDescription === $other->showDescription
			&& $this->showPrice === $other->showPrice
			&& $this->showProviderBadge === $other->showProviderBadge;
	}
}
