<?php
/**
 * Search-result cache port.
 *
 * Phase 9 cache surface for provider search-result payloads,
 * keyed by the canonical search key produced by
 * {@see \Affilun\Application\Cache\CacheKeyFactory::searchKey()}
 * (or its cross-provider sibling). Implementations back the
 * cache layer described in
 * `specifications/sync-and-cache-strategy.md` §4 (cache layers)
 * and §6.3 (cache-key forms).
 *
 * The payload shape is opaque to the cache: callers serialize a
 * normalized search-result associative array on write and
 * receive that same array back on hit. The cache itself does not
 * interpret, validate, or transform the payload — that is the
 * caller's responsibility, kept out of the port so a future
 * payload-shape evolution does not have to change every cache
 * adapter.
 *
 * Eviction is TTL-driven: writes carry an explicit TTL in
 * seconds; reads return null on miss or once the entry's TTL
 * has elapsed. The clock used to apply the TTL is wired into
 * the implementation (typically a
 * {@see \Affilun\Domain\Clock}), not into this
 * port, so consumers do not have to thread "now" through every
 * call.
 *
 * @package Affilun\Application\Cache
 */

declare(strict_types=1);

namespace Affilun\Application\Cache;

use InvalidArgumentException;

/**
 * Phase 9 search-result cache contract.
 *
 * See the file-level docblock for the spec citations, the
 * payload-opacity rule, and the TTL semantics that gate
 * {@see self::get()} hits.
 */
interface SearchCache {

	/**
	 * Read the search-result payload stored under `$key`.
	 *
	 * Returns null on miss, on expiry, or when the implementation
	 * has had its layer flushed since the entry was written.
	 *
	 * @param string $key Cache key, typically produced by
	 *                    {@see CacheKeyFactory::searchKey()} or
	 *                    {@see CacheKeyFactory::crossProviderSearchKey()}.
	 *
	 * @return array<string, mixed>|null Cached payload, or null on miss / expired.
	 */
	public function get( string $key ): ?array;

	/**
	 * Write the search-result payload 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 (it would either store
	 * an entry that is born expired or invite a "store forever"
	 * misinterpretation that the spec deliberately does not grant).
	 *
	 * @param string               $key        Cache key.
	 * @param array<string, mixed> $payload    Normalized search result.
	 * @param int                  $ttlSeconds TTL in seconds; must be > 0.
	 *
	 * @throws InvalidArgumentException When `$ttlSeconds` is non-positive.
	 */
	public function put( string $key, array $payload, 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;
}
