<?php
/**
 * Cache
 *
 * Thin wrapper around WordPress transients.
 * All keys are prefixed with "relatedly_" so wildcard deletion is safe.
 *
 * @package Relatedly\Core
 * @since   2.0.0
 */

namespace Relatedly\Core;

defined( 'ABSPATH' ) || exit;

/**
 * Stores and clears plugin transient data.
 *
 * @since 2.0.0
 */
class Cache {

	const PREFIX = 'relatedly_';

	/**
	 * Get a cached value.
	 *
	 * @param string $key Unprefixed key.
	 * @return mixed|false False on cache miss.
	 */
	public function get( string $key ) {
		return get_transient( self::PREFIX . $key );
	}

	/**
	 * Store a value.
	 *
	 * @param string $key        Unprefixed key.
	 * @param mixed  $value      Value to store.
	 * @param int    $expiration TTL in seconds.
	 */
	public function set( string $key, $value, int $expiration = HOUR_IN_SECONDS ): void {
		set_transient( self::PREFIX . $key, $value, $expiration );
	}

	/**
	 * Delete a single cached value.
	 *
	 * @param string $key Unprefixed key.
	 */
	public function delete( string $key ): void {
		delete_transient( self::PREFIX . $key );
	}

	/**
	 * Flush ALL plugin transients from the options table.
	 * Also calls wp_cache_flush() to clear any persistent object cache.
	 */
	public function flush(): void {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
				$wpdb->esc_like( '_transient_' . self::PREFIX ) . '%',
				$wpdb->esc_like( '_transient_timeout_' . self::PREFIX ) . '%'
			)
		);

		wp_cache_flush();
	}

	/**
	 * Evict the cached related-post pool for a specific post.
	 * Called on save_post so stale pools are immediately invalidated.
	 *
	 * @param int $post_id Post ID.
	 */
	public function flush_post( int $post_id ): void {
		foreach ( [ 'inline', 'end_content', 'rss', 'rss_end', 'floating' ] as $context ) {
			$this->delete( "pool_{$post_id}_{$context}" );
		}
	}
}
