<?php
/**
 * Rendered HTML cache port.
 *
 * Phase 9 cache surface for the rendered output produced by the
 * §22 rendering pipeline, keyed by the canonical render keys
 * produced by
 * {@see \CommerceLinkBuilder\Application\Cache\CacheKeyFactory::renderEmbedKey()}
 * and
 * {@see \CommerceLinkBuilder\Application\Cache\CacheKeyFactory::renderAdhocKey()}.
 * Implementations back the cache layer described in
 * `specifications/sync-and-cache-strategy.md` §4 (cache layers),
 * §6.3 (cache-key forms), and §14 (cache invalidation, including
 * the prefix-wildcard sweep that {@see self::forgetByPrefix()}
 * supports).
 *
 * Cached value shape:
 *
 *   - `view_model`: associative array — the resolved view model
 *     handed to the template engine.
 *   - `html`: string — the rendered HTML produced from that view
 *     model.
 *
 * Storing both halves together lets a hit short-circuit the
 * render pipeline entirely while still preserving the view-model
 * snapshot for diagnostics and for any downstream consumer that
 * wants to re-render with a different template variant without
 * re-fetching upstream data.
 *
 * Eviction is TTL-driven; the clock used to apply the TTL is
 * wired into the implementation. {@see self::forgetByPrefix()}
 * is the targeted-invalidation hook used by the §14
 * `CacheInvalidator` to drop every render entry under a given
 * key prefix (typically a per-product or per-template prefix)
 * without flushing the entire layer.
 *
 * @package CommerceLinkBuilder\Application\Cache
 */

declare(strict_types=1);

namespace CommerceLinkBuilder\Application\Cache;

use InvalidArgumentException;

/**
 * Phase 9 rendered-HTML cache contract.
 *
 * See the file-level docblock for the spec citations, the
 * `['view_model' => array, 'html' => string]` value shape, and
 * the prefix-wildcard invalidation hook used by §14.
 */
interface RenderCache {

	/**
	 * Read the rendered entry stored under `$key`.
	 *
	 * Returns null on miss, on expiry, or when the implementation
	 * has had its layer flushed since the entry was written.
	 *
	 * On hit, the returned array carries exactly two keys:
	 *
	 *   - `view_model` — the associative array snapshot of the
	 *     view model handed to the template engine on the write
	 *     that produced this entry.
	 *   - `html` — the rendered HTML string that the template
	 *     engine produced from that view model.
	 *
	 * @param string $key Cache key, typically produced by
	 *                    {@see CacheKeyFactory::renderEmbedKey()} or
	 *                    {@see CacheKeyFactory::renderAdhocKey()}.
	 *
	 * @return array{view_model: array<string, mixed>, html: string}|null
	 *         Cached entry, or null on miss / expired.
	 */
	public function get( string $key ): ?array;

	/**
	 * Write the rendered entry under `$key` with the given TTL
	 * in seconds.
	 *
	 * Implementations MUST reject `$ttlSeconds <= 0` with
	 * {@see \InvalidArgumentException}; a non-positive TTL has no
	 * meaningful semantics for this layer.
	 *
	 * @param string               $key        Cache key.
	 * @param array<string, mixed> $viewModel  View model snapshot.
	 * @param string               $html       Rendered HTML.
	 * @param int                  $ttlSeconds TTL in seconds; must be > 0.
	 *
	 * @throws InvalidArgumentException When `$ttlSeconds` is non-positive.
	 */
	public function put( string $key, array $viewModel, string $html, int $ttlSeconds ): void;

	/**
	 * Remove the entry stored under `$key`.
	 *
	 * Idempotent: a call against an unknown key is a no-op.
	 *
	 * @param string $key Cache key.
	 */
	public function forget( string $key ): void;

	/**
	 * Drop every entry stored in this cache layer.
	 *
	 * Scope is the layer instance, not the underlying store; if
	 * an implementation shares storage with another layer, the
	 * shared backend MUST still partition entries so flushing
	 * one layer does not also drop a sibling layer's entries.
	 */
	public function flush(): void;

	/**
	 * Drop every entry whose key begins with `$prefix`, returning
	 * the count of removed entries.
	 *
	 * Used by the §14 `CacheInvalidator` to perform targeted
	 * wildcard invalidation (for example, dropping every render
	 * entry under a per-product prefix when that product's
	 * snapshot changes) without flushing the entire layer.
	 *
	 * Behavior on an empty `$prefix` is implementation-defined;
	 * implementations are encouraged to reject it as a defensive
	 * guard against accidental layer-wide flushes.
	 *
	 * @param string $prefix Cache-key prefix.
	 *
	 * @return int Count of removed entries (>= 0).
	 */
	public function forgetByPrefix( string $prefix ): int;
}
